1 Introduction

A comparison of JULES-ES-1p0 wave01 members against the original ensemble (wave00).

Wave 01 input parameter sets were picked using History matching to fall within Andy Wiltshire’s basic constraints on NBP, NPP, cSoil and cVeg stocks at the end of the 20th century. We use 300 of the 500 members, keeping back 2/5ths for emulator validation later.

We answer some basic questions.

What proportion of the new ensemble match AW’s constraints?

What do the timeseries of carbon cycle properties look like with and without AW’s constraints?

How good is a GP emulator? Does it get better overall with the new ensemble members added? In particular, does it get better for those members within the AW constraints?

Does comparison of the ensemble with Atmospheric growth observations give more of a constraint?

To do:

Does the sensitivity analysis change?

2 Preliminaries

Load libraries, functions and data.


ensloc <- '/scratch/hadaw/jules_postprocess/u-ck006/'

makeJulesEnsembleModernValue <- function(ensloc, varlist, nstart, nend, ix = 144:164){
  
  nens <- (nend - nstart) + 1
  datmat <- matrix(nrow = nens, ncol = length(varlist))
  colnames(datmat) <- varlist
  
  enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
  
  for(i in 1:nens){
    
    vec <- rep(NA, length(varlist))
    
    ensmember <- enslist[i] 
    
    fn <- paste0(ensloc,ensmember,'/stats/','JULES-ES-1p0_',ensmember,'_Annual_global.nc')
    
    try(nc <- nc_open(paste0(fn)))
    try(vec <- sapply(varlist, FUN = modernValue, nc = nc, ix = ix))
    datmat[i, ] <- vec
    nc_close(nc)
  }
  return(list(datmat = datmat, enslist = enslist))
}
  
nstart <- 499
nend <- 798

#if (file.exists("ensemble_wave01.rdata")) {
#  load("ensemble_wave01.rdata")
#} else {
  
ens_wave01_mv <- makeJulesEnsembleModernValue(ensloc = '/scratch/hadaw/jules_postprocess/u-ck006/', 
                                              varlist = y_names_sum,
                                              nstart = nstart,
                                              nend = nend, 
                                              ix = 144:164) 
  
#  save(ens_wave01_mv, file ="ensemble_wave01.rdata")
#}
  

lhs_wave01 <- read.table( '../conf_files_augment_JULES-ES-1p0/lhs_example.txt', header = TRUE)

X_wave01 = normalize(lhs_wave01, wrt = rbind(lhs_i, lhs_ii, lhs_wave01))
colnames(X_wave01) = colnames(lhs_wave01)

# Match the 300 outputs we're using in the training data
X_wave01_train <- X_wave01[1:300,]
Y_const_wave01 <- ens_wave01_mv$datmat[, ynames_const]

Y_const_wave01_scaled <- sweep(Y_const_wave01, 2, STATS = scalevec, FUN = '/' )

2.1 How many run failures were there?

There are no NAs but some relative humidity values are infinite. There are no “low NPP” ensemble members


low_npp_ix_wave01 <- which(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'] < 1e5)

min(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'])
[1] 117464.6
Y_wave01_nlevel0_ix <- which(is.na(ens_wave01_mv$datmat[,'nbp_lnd_sum']))

all(is.finite(ens_wave01_mv$datmat))
[1] FALSE
which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)
     row col
[1,] 140   9
[2,] 232   9
[3,] 249   9
[4,] 300   9
ens_wave01_mv$datmat[which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)]
[1] Inf Inf Inf Inf
colnames(ens_wave01_mv$datmat)[9]
[1] "rh_lnd_sum"

3 Ensemble behaviour in key (constraining) outputs.

Global mean for the 20 years at the end of the 20th Century. There is still a significant low bias on cVeg output.


wave00col <- 'skyblue2'
wave01col <- 'tomato2'

wave00col <- 'dodgerblue2'
wave01col <- 'firebrick'
rangecol <- 'grey'


# Histogram of level 1 constraints
hcol = 'darkgrey'
lcol = 'black'

#pdf(file = 'figs/level_2_constraints_hists.pdf', width = 6, height = 5)
par(mfrow = c(2,2), fg = 'darkgrey', las = 1, oma = c(0.1, 0.1, 4, 0.1))

trunc <- function(x, vec){
  
  dat <- x[x < max(vec) & x > min(vec)  ]
  
  dat
  
}


h <- hist(Y_const_level1a_scaled[,'nbp_lnd_sum'], main = 'NBP', xlab = 'GtC/year', col = makeTransparent(wave00col,150))
hist(trunc(Y_const_wave01_scaled [,'nbp_lnd_sum'], h$breaks) ,
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['nbp_lnd_sum'], lwd = 2)

polygon(x = c(0, 100, 100, 0), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'npp_nlim_lnd_sum'],col = makeTransparent(wave00col,150), main = 'NPP', xlab = 'GtC/year')
hist(trunc(Y_const_wave01_scaled [,'npp_nlim_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['npp_nlim_lnd_sum'], lwd = 2)

polygon(x = c(35, 80, 80, 35), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))


h <- hist(Y_const_level1a_scaled[,'cSoil_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Soil Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cSoil_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cSoil_lnd_sum'], lwd = 2)

polygon(x = c(750, 3000, 3000, 750), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'cVeg_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Vegetation Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cVeg_lnd_sum'], h$breaks) , 
   col = makeTransparent(wave01col,150)  , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cVeg_lnd_sum'], lwd = 2)

polygon(x = c(300, 800, 800, 300), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
       border =  makeTransparent(rangecol))

#dev.off()

reset()

legend('top', horiz = TRUE, fill = c(makeTransparent(wave00col, 150), makeTransparent(wave01col, 150), makeTransparent(rangecol, 60)), legend = c('Wave00', 'Wave01', 'AW range'))

3.1 What proportion of models now fall within Andy’s constraints?

A third! Better than before, but still not great. Pointing at a significant model discrepency in cVeg

AW_constraints <- matrix(nrow = 2, ncol = length(ynames_const))

AW_constraints[1,] <- c(0, 35, 750, 300)
AW_constraints[2,] <- c(100, 80, 3000, 800)

colnames(AW_constraints) <- ynames_const
rownames(AW_constraints) <- c('min', 'max')


# conform to Andy's basic constraints
#level2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes  = AW_constraints[2,], mins = AW_constraints[1,] ))

#nlevel2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes  = AW_constraints[2,], mins = AW_constraints[1,] ) == FALSE)

level2_ix_wave01 <- which(Y_const_wave01_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_wave01_scaled[,'cVeg_lnd_sum'] < 800
                  )

Of the 300 members of the wave01 ensemble, 100 pass Andy Wiltshire’s Level 2 constraints.


length(level2_ix_wave01)
[1] 100

Pairs plot of the inputs that pass the constraints with respect to the limits of the original ensemble.


pairs(X_wave01[level2_ix_wave01, ],
      xlim = c(0,1),
      ylim = c(0,1),
      gap = 0,
      lower.panel = NULL,
      pch = 20,
      col = makeTransparent(wave01col,200)
      )

3.2 Timeseries of mean carbon cycle properties over whole run.


## ----------------------------------------------------------------------
## Load ensemble
## ----------------------------------------------------------------------

if (file.exists("ensemble_timeseries_wave01.rdata")) {
  load("ensemble_timeseries_wave01.rdata")
} else {
  
  # primary carbon cycle outputs
  npp_ens <- makeTimeseriesEnsemble(variable = "npp_nlim_lnd_sum") / (1e12/ysec)
  nbp_ens <-  makeTimeseriesEnsemble(variable = "nbp_lnd_sum") / (1e12/ysec)
  cSoil_ens <-  makeTimeseriesEnsemble(variable = "cSoil_lnd_sum") / 1e12
  cVeg_ens <-  makeTimeseriesEnsemble(variable = "cVeg_lnd_sum") / 1e12
  
  
  lai_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "lai_lnd_mean")
  
  # fluxes
  rh_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "rh_lnd_sum") / (1e12/ysec)
  fLuc_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "fLuc_lnd_sum") / (1e12/ysec)
  fHarvest_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "fHarvest_lnd_sum") / (1e12/ysec)
  
  
  # fractions
  treeFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "treeFrac_lnd_mean")
  shrubFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "shrubFrac_lnd_mean")
  baresoilFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "baresoilFrac_lnd_mean")
  #c3PftFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "c3PftFrac_lnd_mean_ens")
  #c4PftFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "c4PftFrac_lnd_mean_ens")
  
  save(npp_ens, nbp_ens, cSoil_ens, cVeg_ens, lai_lnd_mean_ens, rh_lnd_sum_ens, fLuc_lnd_sum_ens, fHarvest_lnd_sum_ens,
       treeFrac_lnd_mean_ens, shrubFrac_lnd_mean_ens, baresoilFrac_lnd_mean_ens, file = "ensemble_timeseries_wave01.rdata" )
  
}

total_land_carbon_ens <- cSoil_ens + cVeg_ens


makeTimeseriesEnsemble <- function(ensloc, variable, nstart, nend, nts = 164, cn = 1850:2013){
  
  nens <- (nend - nstart) + 1
  # nens is number of ensemble members
  # nts length of timeseries
  # cn is colnames()
  datmat <- matrix(NA, nrow = nens, ncol = nts)
  colnames(datmat) <- cn
  
  enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
  #floc <- paste0(ensloc,ensmember,subdir)
  
  for(i in 1:nens){
    
    vec <- rep(NA,nts)
    
    ensmember <- enslist[i] 
    
    fn <- paste0(ensloc,ensmember,'/stats/','JULES-ES-1p0_',ensmember,'_Annual_global.nc')
    
    
    try(nc <- nc_open(paste0(fn)))
    try(dat <- extractTimeseries(nc, variable))
    
    datmat[i, ] <- dat
    nc_close(nc)
  }
  datmat
}

nstart <- 499
nend <- 798

if (file.exists("ensemble_timeseries_wave01.rdata")) {
  load("ensemble_timeseries_wave01.rdata")
} else {
  
  # primary carbon cycle outputs
   npp_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "npp_nlim_lnd_sum") / (1e12/ysec)
   nbp_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "nbp_lnd_sum") / (1e12/ysec)
   cSoil_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "cSoil_lnd_sum") / 1e12
   cVeg_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "cVeg_lnd_sum") / 1e12
  # 
  # 
   lai_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "lai_lnd_mean")
  # 
  # # fluxes
   rh_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "rh_lnd_sum") / (1e12/ysec)
   fLuc_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "fLuc_lnd_sum") / (1e12/ysec)
   fHarvest_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "fHarvest_lnd_sum") / (1e12/ysec)
  # 
  # 
  # # fractions
   treeFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "treeFrac_lnd_mean")
   shrubFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "shrubFrac_lnd_mean")
   baresoilFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "baresoilFrac_lnd_mean")
  
   save(npp_ens_wave01,
        nbp_ens_wave01,
        cSoil_ens_wave01,
        cVeg_ens_wave01,
        lai_lnd_mean_ens_wave01,
        rh_lnd_sum_ens_wave01,
        fLuc_lnd_sum_ens_wave01,
        fHarvest_lnd_sum_ens_wave01,
        treeFrac_lnd_mean_ens_wave01,
        shrubFrac_lnd_mean_ens_wave01,
        baresoilFrac_lnd_mean_ens_wave01,
       file = "ensemble_timeseries_wave01.rdata" )
   
}

#total_land_carbon_ens_wave01 <- cSoil_ens_wave01 + cVeg_ens_wave01

lcol_wave0 <- makeTransparent('dodgerblue2',  120)
lcol_wave01 <- makeTransparent('firebrick',  120)
lcol_wave01_level2 <- 'gold'
stancol = 'black'

linePlotMultiEns <- function(years, ens1, ens2, ens3, col1, col2, col3, ylab, main, ylim = NULL){
  # Plot wave00 and wave01 timeseries on top of one another
  
  nt <- length(years) 
  if(is.null(ylim)){
    
  ylim = range(c(ens1[,1], ens1[,nt], ens2[,1], ens2[ ,nt], ens3[,1], ens3[, nt]))
  }
  
  else ylim <- ylim
  
  matplot(years, t(ens1), type = 'l', lty = 'solid',ylim = ylim, col = col1,
        ylab = ylab, main = main, xlab = '',
        bty = 'n')
  matlines(years, t(ens2), col = col2, lty = 'solid')
    matlines(years, t(ens3), col = col3, lty = 'solid')
}


par(mfrow= c(3,4), las = 1)

linePlotMultiEns(years = years, ens1 = npp_ens, ens2 = npp_ens_wave01, ens3 = npp_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NPP')

lines(years,npp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 =  nbp_ens, ens2 = nbp_ens_wave01, ens3 = nbp_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NBP', ylim = c(-10,10))

lines(years, nbp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cSoil_ens, ens2 = cSoil_ens_wave01, ens3 = cSoil_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens[,1], cSoil_ens[,164])))

lines(years, cSoil_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cVeg_ens, ens2 = cVeg_ens_wave01, ens3 = cVeg_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cVeg')

lines(years, cVeg_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens, ens2 = lai_lnd_mean_ens_wave01, ens3 = lai_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'Lai')

lines(years, lai_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens, ens2 = rh_lnd_sum_ens_wave01, ens3 = rh_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,  col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'RH')

lines(years, rh_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens, ens2 = fLuc_lnd_sum_ens_wave01, ens3 = fLuc_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fLuc')

lines(years, fLuc_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens, ens2 = fHarvest_lnd_sum_ens_wave01,
                 ens3 = fHarvest_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fHarvest')

lines(years, fHarvest_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens, ens2 = treeFrac_lnd_mean_ens_wave01,
                 ens3 = treeFrac_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'treefrac'
                 )

lines(years, treeFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens, ens2 = shrubFrac_lnd_mean_ens_wave01,
                 ens3 = shrubFrac_lnd_mean_ens[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'shrubfrac'
)

lines(years, shrubFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens, ens2 = baresoilFrac_lnd_mean_ens_wave01,
                 ens3 = baresoilFrac_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'baresoilfrac')

lines(years, baresoilFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

reset()

legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )

NA
NA

This is a plot of timeseries of Wave00, Wave01, and level2-constrained wave01 on top of one another. We see that the wave01 is closer to the standard than wave00, and the level-2 constrained wave01 ensemble is often closer again. However, there are still quite large discrepancies. For example, baresoilfrac is often way too high, shrubfrac is often too low (though both these span the standard). Treefrac is away from zero, but still often too low or too high. While fHarvest looks good, fLuc does not appear constrained by the process at all. RH (soil respiration) looks well constrained, whereas lai is often too low.

One thing we could do next is constrain input space again, using observations or “tolerance to error” on some or all of these outputs.

We could also extend sensitivity analysis to work out what controls e.g. treefrac.

4 Emulator fits

We hope that running the new ensemble gives us a better emulator, and allows us to rule out more input space. We particularly hope that the emulator is better for those members that are inside AW’s constraints.

First, we can look at the emulator errors in two cases: The level1a data (a basic carbon cycle), and then with the Wave01 data, which should have similar characteristics. (We should have eliminated really bad simulations, but wave01 is not constrained the data perfectly to be within AW constraints.)

#fit_list_const_level1a <- createKmFitList(X = X_level1a, Y = Y_const_level1a_scaled)

Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)

fit_list_const_level1a <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a,
                                   mc.cores = 4, control = list(trace = FALSE))

# Bind together the input matrices and scaled output data
X_level1a_wave01 <- rbind(X_level1a, X_wave01_train)[-440, ]

Y_const_level1a_wave01_scaled <- rbind(Y_const_level1a_scaled, Y_const_wave01_scaled)[-440, ]


apply(Y_const_level1a_wave01_scaled ,2, which.max)
     nbp_lnd_sum npp_nlim_lnd_sum    cSoil_lnd_sum     cVeg_lnd_sum 
             583              213              317              312 
apply(Y_const_level1a_wave01_scaled ,2, which.min)
     nbp_lnd_sum npp_nlim_lnd_sum    cSoil_lnd_sum     cVeg_lnd_sum 
             314              243              243              243 

Found the outlier - looks like it’s 440


findOutliers <- function(x, sds = 6){
  
  ix <- which(abs(x - mean(x)) > (sds * sd(x)))
  
}

apply(Y_const_level1a_wave01_scaled ,2, findOutliers, sds = 10)
integer(0)
# Create fit lists for the combined data
Y_const_level1a_wave01_scaled_list <- mat2list(Y_const_level1a_wave01_scaled)

fit_list_const_level1a_wave01 <- mclapply(X = Y_const_level1a_wave01_scaled_list , FUN = km, formula = ~., design = X_level1a_wave01,
                                   mc.cores = 4, control = list(trace = FALSE))

4.1 Leave-one-out analyses of emulator prediction accuracy


loolist_km_Y_level1a <- mclapply(X = fit_list_const_level1a, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)

loolist_km_Y_level1a_wave01 <- mclapply(X = fit_list_const_level1a_wave01, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)

loostats_km_Y_level1a <- lapply(fit_list_const_level1a, FUN = kmLooStats)
loostats_km_Y_level1a_wave01 <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStats)

The top row shows the leave-one-out prediction accuracy of the original wave00 ensemble, and the lower row the entire wave00 AND wave01 ensemble combined.


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_scaled[, i]
  loo <- loolist_km_Y_level1a[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 50))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

#dev.off()

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_wave01_scaled[, i]
  loo <- loolist_km_Y_level1a_wave01[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 100))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

# remove to level 1a Relative to toplevel_ix - useful for plotting etc.
#toplevel_to_level_1a_ix <-(toplevel_ix[-Y_nlevel0_ix])[level1a_ix]

# So further constraining to level 2 can be associated back to the top level.

level2_ix <- which(Y_const_level1a_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] > 35 &  Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_level1a_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_level1a_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_scaled[,'cVeg_lnd_sum'] < 800
                  
  )

level2_ix_level1a_wave01 <- which(Y_const_level1a_wave01_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] < 800
                  )

4.2 Emulator accuracy of members from wave 00 and wave 01 that pass level 2 (AW’s) constraints

We see that the error stats for some of the outputs from wave01 are worse, but there are many more ensemble members that lie within the constraints for wave 01.

“pmae” is “proportional mean absolue error”, which is the mean absolute error expressed as a percentage of the original (minimally constrained) ensemble range in that output.


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]
  loo <- loolist_km_Y_level1a[[i]]
  ylim <- range(c(loo$mean[level2_ix] - (2*loo$sd[level2_ix]), loo$mean[level2_ix] + (2*loo$sd[level2_ix])) )
  plot(y, loo$mean[level2_ix], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean[level2_ix] - (2*loo$sd[level2_ix])  , x1 = y , y1 = loo$mean[level2_ix] + (2*loo$sd[level2_ix]), col = makeTransparent(wave00col, 100))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

#dev.off()

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_wave01_scaled[level2_ix_level1a_wave01, i]
  loo <- loolist_km_Y_level1a_wave01[[i]]
  ylim <- range(c(loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01]), loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01])) )
  plot(y, loo$mean[level2_ix_level1a_wave01], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01])  , x1 = y , y1 = loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01]), col = makeTransparent(wave01col, 50))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 2 constrained ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

4.3 Does the emulator improve is you look at only the 37 members that pass level 2 constraints in wave 00?

This gives us an idea of how good the emulator is where it really matters, and as the members are consistent, gives us a fairer idea of whether the emulators have improved with more members.

Good news is, the emulators are more accurate for wave01.



kmLooStatsSubset <- function (km, ix, type = "UK") 
{
  # Calculate summary statistics for a subset of the members of a km fit list
    loo <- leaveOneOut.km(km, type = type, trend.reestim = TRUE)
    preddiff <- loo$mean[ix] - km@y[ix]
    mae <- mean(abs(preddiff))
    rmse <- sqrt(mean(preddiff^2))
    maxerr <- max(preddiff)
    absdiff <- abs(diff(range(km@y)))
    pmae <- (mae/absdiff) * 100
    return(list(loo = loo, mae = mae, pmae = pmae, maxerr = maxerr))
}

loolist_km_Y_level1a_level2 <- rapply(loolist_km_Y_level1a, f = function(x) x[level2_ix], how = "list")

loolist_km_Y_level1a_wave01_level2 <- rapply(loolist_km_Y_level1a_wave01, f = function(x) x[level2_ix], how = "list")

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,2), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]
  
  loo <- loolist_km_Y_level1a_level2[[i]]
  ylim <- range(c(loo$mean- (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 250),
       pch = 19)
  arrows(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 150) ,  angle = 90, code = 3, length = 0.03)
  
  y1 <- Y_const_level1a_wave01_scaled[level2_ix, i]
  loo <- loolist_km_Y_level1a_wave01_level2[[i]]
  
    points(y1, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 250),
       pch = 19)
  arrows(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 250),  angle = 90, code = 3, length = 0.03)
  
  
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 2 wave 00 ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

reset()
legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)

NA
NA

These leave-one-out prediction accuracy plots rank the ensemble members from largest underprediction to largest overprediction using the wave00 predictions. A perfect prediction would appear on the horizontal “zero” line.

Many of the wave01 predictions are closer to the horizontal line, and therefore more accurate predictions.

None of the predictions are outside the uncertainty bounds, which suggests they are overconservative (should be smaller).


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(4,1), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]

  loo_00 <- loolist_km_Y_level1a_level2[[i]]
  loo_01 <- loolist_km_Y_level1a_wave01_level2[[i]]
  
  preddiff_wave00 <- y - loo_00$mean
  preddiff_wave01 <- y - loo_01$mean
  
    # rank by the original wave 00 predictions
  loo_rank_ix <- sort(preddiff_wave00 , index.return = TRUE)
  
   ylim <- range(c(preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix]),
                   preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]),
                   preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix]),
                   preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix])
                   )
                 )
   
   plot(preddiff_wave00[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave00col, 255),
        pch = 19, ylim = ylim)
   
   abline(h = 0)
   
  arrows(x0 = 1:length(y), y0 = preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix])  , x1 = 1:length(y) , y1 = preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]), col = makeTransparent(wave00col, 150),  angle = 90, code = 3, length = 0.03)
   
  points(preddiff_wave01[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave01col, 255),
        pch = 19)
  
    arrows(x0 = 1:length(y), y0 = preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix])  , x1 = 1:length(y) , y1 = preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix]), col = makeTransparent(wave01col, 150), angle = 90, code = 3, length = 0.03)
   
   mtext(colnames(Y_const_level1a_scaled)[i], side = 3, adj = 0, line = 1)
  

}


 reset()
 legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)


loostats_km_Y_level1a_sub <- lapply(fit_list_const_level1a, FUN = kmLooStatsSubset, ix = level2_ix)
loostats_km_Y_level1a_wave01_sub <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStatsSubset, ix = level2_ix)

Looking at the proportional mean absolute error (pmae), expressed in percent, we can see that it doesn’t improve much for the whole ensemble, but does improve significantly for the subset of ensemble members that fall within AW’s constraints from the first ensemble (marked "_sub").


pmae_wave00 <- lapply(loostats_km_Y_level1a, FUN = function(x) x$pmae )
pmae_wave01 <- lapply(loostats_km_Y_level1a_wave01, FUN = function(x) x$pmae )

pmae_wave00_sub <- lapply(loostats_km_Y_level1a_sub, FUN = function(x) x$pmae )
pmae_wave01_sub <- lapply(loostats_km_Y_level1a_wave01_sub, FUN = function(x) x$pmae )

pmae_table <- cbind(pmae_wave00, pmae_wave01, pmae_wave00_sub, pmae_wave01_sub)

print(pmae_table)
     pmae_wave00 pmae_wave01 pmae_wave00_sub pmae_wave01_sub
[1,] 5.084153    4.927634    7.17902         4.913062       
[2,] 4.282042    4.007528    4.804363        4.085545       
[3,] 3.597314    3.790255    4.555892        3.833982       
[4,] 4.240908    4.515949    4.814           3.226669       

5 Comparing atmospheric growth in wave00, wave01 and observations


obscol = 'purple'
wave01_level2col <- 'gold'

ag <- matrix(nrow = nrow(nbp_ens), ncol = ncol(nbp_ens))

ag01 <- matrix(nrow = nrow(nbp_ens_wave01), ncol = ncol(nbp_ens_wave01))

for(i in 1:nrow(nbp_ens)){
  
ag[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_ens[i, ]
  
}


for(i in 1:nrow(nbp_ens_wave01)){
ag01[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_ens_wave01[i, ]
  
}

ag_stan <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_stan

#pdf(width = 8, height = 6, file = 'ag.pdf')
matplot(years, t(ag), type = 'l', lty = 'solid',ylim = c(-2, 10), col = makeTransparent(wave00col,100), 
         ylab = 'GtC', main = 'atmospheric growth', xlab = '',
         bty = 'n', xlim = c(1960, 2013))

matlines(years, t(ag01), col = makeTransparent(wave01col,100), lty = 'solid')

matlines(years, t(ag01[level2_ix_wave01, ]), col = makeTransparent(wave01_level2col, 100) , lty = 'solid')

lines(historical_carbon_budget$Year, historical_carbon_budget$`atmospheric growth`, col = obscol, lwd =2)

lines(years, ag_stan, col = stancol, lwd =2)


legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard', 'GCB'), col = c(wave00col, wave01col, wave01_level2col, stancol, obscol), lty = 'solid', lwd = 2)

#dev.off()

5.1 Andy asks - what constraint does that give us in cumulative NBP?


cumulative_nbp_ens <- t(apply(nbp_ens, 1, cumsum))

cumulative_nbp_ens_wave01 <- t(apply(nbp_ens_wave01, 1, cumsum))

cumulative_nbp_stan <- cumsum(nbp_stan)

matplot(years, t(cumulative_nbp_ens), type = 'l', lty = 'solid',ylim = c(-130, 250), col = makeTransparent(wave00col,150), 
         ylab = 'GtC', main = 'cumulative NBP', xlab = '',
         bty = 'n', xlim = c(1850, 2020))

matlines(years, t(cumulative_nbp_ens_wave01),
         col = makeTransparent(wave01col,150),
         lty = 'solid')

matlines(years, t(cumulative_nbp_ens_wave01[level2_ix_wave01, ]),
         col = makeTransparent(wave01_level2col, 150),
         lty = 'solid')


lines(years, cumulative_nbp_stan, col = makeTransparent(stancol, 200), lty = 'solid', lwd = 1.5 )


legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)

5.2 Eddy suggests measuring cumulative NBP against atmospheric growth rate


# Cumulative NBP at the end of the run
cnbp_modern_ens <- apply(cumulative_nbp_ens[, 135:164], 1, mean)

cnbp_modern_wave01 <- apply(cumulative_nbp_ens_wave01[, 135:164], 1, mean)

cnbp_modern_stan <- mean(cumulative_nbp_stan[135:164])

Calculate the atmospheric growth rate of 1984- 2013 using a simple linear fit

# agr = atmospheric growth rate
# agiav = atmospheric growth interannual variability

agr_modern_ens <- rep(NA, length = nrow(ag))
agiav_modern_ens <- rep(NA, length = nrow(ag))

for(i in 1:nrow(ag)){
  
  dat <- data.frame(t =1:30, ag =  ag[i, 135:164])
  fit <- lm(ag ~ t, data = dat)
  agr_modern_ens[i] <- coef(fit)['t']
  agiav_modern_ens[i] <- sd(fit$residuals)
}


agr_modern_wave01 <- rep(NA, length = nrow(ag01))
agiav_modern_wave01 <- rep(NA, length = nrow(ag01))

for(i in 1:nrow(ag01)){
  
  dat <- data.frame(t =1:30, ag =  ag01[i, 135:164])
  fit <- lm(ag ~ t, data = dat)
  agr_modern_wave01[i] <- coef(fit)['t']
  agiav_modern_wave01[i] <- sd(fit$residuals)
}

dat <- data.frame(t =1:30, ag =  ag_stan[135:164])
agr_stan_fit <- lm(ag ~ t, data = dat) 
agr_stan <- coef(agr_stan_fit)['t']
agiav_stan <- sd(agr_stan_fit$residuals)
  
  

plot(agr_modern_ens, cnbp_modern_ens,  col = makeTransparent(wave00col,150),
     ylim = c(-120,220), pch = 19,
     xlab = 'Atmospheric growth rate 1984-2014',
     ylab = 'Cumulative NBP from preindustrial by 1984-2013'
     )

cnbp_wave01_cols <- rep(wave01col, length(cnbp_modern_wave01))
cnbp_wave01_cols[level2_ix_wave01] <- wave01_level2col 

points( agr_modern_wave01,cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agr_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)

legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)


print(c('correlation agr vs cnbp (all members)', cor(agr_modern_ens, cnbp_modern_ens)))
[1] "correlation agr vs cnbp (all members)" "-0.0407011946805412"                  
print(c('correlation agr vs cnbp (wave01)', cor(agr_modern_wave01, cnbp_modern_wave01)))
[1] "correlation agr vs cnbp (wave01)" "0.00334447281747124"             
fit_wave00 <- lm(agr_modern_ens ~ cnbp_modern_ens)
fit_wave01 <- lm(agr_modern_wave01 ~ cnbp_modern_wave01)

print(summary(fit_wave00))

Call:
lm(formula = agr_modern_ens ~ cnbp_modern_ens)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.047413 -0.003802  0.003838  0.006715  0.023090 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      1.114e-01  5.176e-04 215.173   <2e-16 ***
cnbp_modern_ens -1.379e-09  1.519e-09  -0.908    0.364    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01155 on 497 degrees of freedom
Multiple R-squared:  0.001657,  Adjusted R-squared:  -0.0003522 
F-statistic: 0.8247 on 1 and 497 DF,  p-value: 0.3643
print(summary(fit_wave01))

Call:
lm(formula = agr_modern_wave01 ~ cnbp_modern_wave01)

Residuals:
    Min      1Q  Median      3Q     Max 
  -4639   -4639   -4639   -4639 1382335 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)
(Intercept)        4.639e+03  4.639e+03   1.000    0.318
cnbp_modern_wave01 5.475e-29  9.483e-28   0.058    0.954

Residual standard error: 80210 on 298 degrees of freedom
Multiple R-squared:  1.119e-05, Adjusted R-squared:  -0.003344 
F-statistic: 0.003333 on 1 and 298 DF,  p-value: 0.954

Interannual variability and cumulative NBP

(correlations are close to zero, especially in the later wave)


plot(agiav_modern_ens, cnbp_modern_ens,  col = makeTransparent(wave00col,150),
     ylim = c(-120,220), pch = 19,
     xlab = 'Atmospheric growth IAV 1984-2014',
     ylab = 'Cumulative NBP from preindustrial by 1984-2013'
     )

points( agiav_modern_wave01, cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agiav_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)

legend('bottomright', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)



print(cor(agiav_modern_ens, cnbp_modern_ens))
[1] 0.03223847
print(cor( agiav_modern_wave01, cnbp_modern_wave01))
[1] 0.003344484

5.3 How close can we get the model to reality?

Using Atmospheric Growth Rate as an example, how close can we get the model to observations? Can we do better than standard? What are the trade offs of doing so? How does getting close in AGR affect performance in other outputs?

# Define the observed atmospheric growth rate.

ag_obs <- historical_carbon_budget$`atmospheric growth`[which(historical_carbon_budget$Year %in% years)]

# Model departure from observations of atmospheric growth
ag_err <- sweep(ag, 2, ag_obs, FUN = '-')
ag01_err <- sweep(ag01, 2, ag_obs, FUN = '-')


long_modern_years <- 1960:2013
ag_modern_ix <- which( years %in% long_modern_years)

ag_err_modern <- ag_err[, ag_modern_ix]
ag01_err_modern <- ag01_err[, ag_modern_ix]


ag_err_stan <- ag_stan - ag_obs
ag_err_stan_modern <- ag_err_stan[ag_modern_ix]
matplot(long_modern_years, t(ag_err_modern), type = 'l', lty = 'solid', col = makeTransparent(wave00col, 100), main = 'Amospheric Growth Error')

errcol <- rep(wave01col, nrow(ag01))
#errcol[level2_ix_wave01] <- wave01_level2col
matlines(long_modern_years, t(ag01_err_modern), col = makeTransparent(errcol, 100), lty = 'solid')
matlines(long_modern_years, t(ag01_err_modern[level2_ix_wave01, ]), col = makeTransparent(wave01_level2col,100), lty = 'solid')

lines(long_modern_years, ag_err_stan_modern, col = stancol)

legend('bottomleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)

abline(h=0)


# Atmospheric growth mean error
ag_modern_me <- apply(ag_err_modern,1,mean)
ag01_modern_me <- apply(ag01_err_modern,1, mean)
ag_modern_me_stan <- mean(ag_err_stan[ag_modern_ix])


# Mean absolute error
ag_modern_mae <- apply(abs(ag_err_modern),1,mean)
ag01_modern_mae <- apply(abs(ag01_err_modern),1, mean)
ag_modern_mae_stan <- mean(abs(ag_err_stan[ag_modern_ix]))



# Root mean square error
ag_modern_rmse <- apply(ag_err_modern,1, function(x) sqrt(mean(x^2)))
ag01_modern_rmse <- apply(ag01_err_modern,1, function(x) sqrt(mean(x^2)))
ag_modern_rmse_stan <- sqrt(mean(ag_err_stan[ag_modern_ix]^2))

# There are some big outliers 
outlier_ix_wave01 <- which(abs(ag01_modern_me )> 100)

par(mfrow = c(2,1))
hist(ag_modern_me, main = 'Atmospheric growth mean error', col = wave00col, xlim = c(-3, 3))
rug(ag_modern_me_stan, col = stancol, lwd = 2)
hist(ag01_modern_me[-outlier_ix_wave01], col = wave01col, xlim = c(-3,3), main = '')
rug(ag_modern_me_stan, col = stancol, lwd = 2)

par(mfrow = c(2,1))

hist(ag_modern_mae, main = 'Atmospheric growth mean absolute error', col = wave00col, xlim = c(0, 3))
rug(ag_modern_mae_stan, col = stancol, lwd = 2)
hist(ag01_modern_mae[-outlier_ix_wave01], col = wave01col, lwd = 2, xlim = c(0,3), main = '')
rug(ag_modern_mae_stan, col = stancol, lwd = 2)

par(mfrow = c(2,1))

hist(ag_modern_rmse, xlim = c(0,3), col = wave00col, main = 'Atmospheric growth RMSE')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )
hist(ag01_modern_rmse[-outlier_ix_wave01], col = wave01col, xlim = c(0,3), main = '')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )

NA
NA
NA

We’ve established that most of the original ensemble have an ME/MAE/RMSE larger than the standard run. More (but few) of the wave01 perform better than standard.



better_ix_ag_rmse <- which(ag_modern_rmse < ag_modern_rmse_stan)
better_ix_ag01_rmse <- which(ag01_modern_rmse < ag_modern_rmse_stan)


X_better_ag <- rbind(X[better_ix_ag_rmse, ], X_wave01_train[better_ix_ag01_rmse, ])

A map of the 2D projections of parameter space where the ensemble member performs better than standard.

The blue part is the first wave, and not subject to constraint so may be removed in the second wave (wave01).


better_ix <- 1:nrow(X_better_ag)

better_cols <- c(rep(wave00col, length(better_ix_ag_rmse)), rep(wave01col,length(better_ix_ag01_rmse)))

pairs(X_better_ag,
      col = better_cols,
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)

NA
NA
NA

5.4 Build emulators and find parts of parameter space that to better than standard at atmospheric growth.

Having trouble fitting RMSE, to trying mean error.

Why is there an odd collection at just under 1?

# there are 100 wave01 ensemble members that pass the level 2 constrainst
length(level2_ix_wave01)
[1] 100
better_ix_ag_me <- which(abs(ag_modern_me) < abs(ag_modern_me_stan))

# There are 104 that have a smaller mean error than standard
better_ix_ag01_me <- which(abs(ag01_modern_me) < abs(ag_modern_me_stan))

# There are only 41 that pass level 2 AND have smaller error than standard
level2_and_better_ag01_ix <- intersect(level2_ix_wave01, better_ix_ag01_me)

This next pairs plot looks at all the ensemble members that have a better mean atmospheric growth error than standard.

X_better_ag <- rbind(X[better_ix_ag_me, ], X_wave01_train[better_ix_ag01_me, ])

better_ix <- 1:nrow(X_better_ag)

better_cols <- c(makeTransparent(rep(wave00col, length(better_ix_ag_me)), 100), makeTransparent(rep(wave01col,length(better_ix_ag01_me)), 100))

pairs(X_better_ag,
      col = better_cols,
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)

This next plot looks at all the ensemble members that have a better mean atmospheric growth error than standard AND pass the level 2 constraints.

The number is small (41/300), but the ensemble members seem spread across parameter space.


X_level2_and_better_ag01 <- X[level2_and_better_ag01_ix, ]
pairs(X_level2_and_better_ag01,
      col = makeTransparent('black', 150),
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)


plot(fit_ag_me)

NA
NA
NA
NA
(length(X_kept_ix) / nunif) * 100
[1] 21.057

5.5 Input space with low Atmospheric Growth Error

This pairs plot shows the 2d and marginal density of emulated input points where the emulated atmospheric growth is closer to the observations than the standard model.

This technique might provide a useful set of points for optimising the model (at least to atmospheric growth).

#pairs(X_unif[X_kept_ix, ][1:50,],
#      col = makeTransparent('black',50),
#      gap = 0,
#      xlim = c(0,1), ylim = c(0,1),
#      pch = 19,
#      cex= 0.8,
#      lower.panel = NULL)

par(oma = c(0,0,0,3), bg = 'white')


panel.hist = function(x, ...) {
  usr = par("usr"); on.exit(par(usr))
  par(usr = c(usr[1:2], 0, 1.5))
  hist(x, freq = FALSE, col="cyan", add=TRUE) 
  lines(density(x))
}

pairs(X_unif[X_kept_ix, ],
    #  labels = 1:d,
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = dfunc_up,
      diag.panel = panel.hist,
      cex.labels = 1,
      col.axis = 'white',
      dfunc_col = rb)

Next, check emulators of all the other outputs and apply the constraints to them. See how the constraints change.



# First, try the emulators built using just wave01
#fit_list_const_level1a


Y_const_pred_unif_mean <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif))
colnames(Y_const_pred_unif_mean) <- colnames(Y_const_level1a_scaled)

Y_const_pred_unif_sd <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif)) 
colnames(Y_const_pred_unif_sd) <- colnames(Y_const_level1a_scaled)

for(i in 1:length(fit_list_const_level1a)){
  
  pred_unif <- predict.km(object=fit_list_const_level1a[[i]], newdata = X_unif, type = 'UK')
  
  Y_const_pred_unif_mean[,i ] <- pred_unif$mean
  Y_const_pred_unif_sd[,i ] <- pred_unif$sd
}

5.6 Input space with emulated members passing Level 2 constraints.

(length(level2_ix_em) / nunif) * 100
[1] 12.985

5.7 Input space with emulated members passing Level 2 constraints AND low atmospheric growth error

Emulated members passing level2 constraints AND having lower error in atmospheric growth than standard.

Red point indicates the standard input.


X_stan_norm <- normalize(matrix(rep(1, 32), nrow = 1), wrt = lhs)

level2_and_ag_ix_em <- which(Y_const_pred_unif_mean[,'nbp_lnd_sum'] > 0 &
                    Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] > 35 & Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_pred_unif_mean[,'cSoil_lnd_sum'] > 750 & Y_const_pred_unif_mean[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_pred_unif_mean[,'cVeg_lnd_sum'] > 300 & Y_const_pred_unif_mean[,'cVeg_lnd_sum'] < 800 &
                    abs(pred_unif_ag$mean) < abs(ag_modern_me_stan)
                  )



pairs(rbind(X_unif[level2_and_ag_ix_em, ], X_stan_norm),
    #  labels = 1:d,
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = dfunc_up_truth,
      diag.panel = panel.hist,
      cex.labels = 1,
      col.axis = 'white',
      dfunc_col = rb)



(length(level2_and_ag_ix_em) / nunif) * 100
[1] 5.446
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgSlVMRVMxcDAgZW5zZW1ibGUgd2F2ZTAxIgpvdXRwdXQ6IAogICAgaHRtbF9ub3RlYm9vazoKICAgICAgICB0b2M6IHRydWUKICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICB0b2NfZGVwdGg6IDMKICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgojIEludHJvZHVjdGlvbgoKQSBjb21wYXJpc29uIG9mIEpVTEVTLUVTLTFwMCB3YXZlMDEgbWVtYmVycyBhZ2FpbnN0IHRoZSBvcmlnaW5hbCBlbnNlbWJsZSAod2F2ZTAwKS4KCldhdmUgMDEgaW5wdXQgcGFyYW1ldGVyIHNldHMgd2VyZSBwaWNrZWQgdXNpbmcgSGlzdG9yeSBtYXRjaGluZyB0byBmYWxsIHdpdGhpbiBBbmR5IFdpbHRzaGlyZSdzIGJhc2ljIGNvbnN0cmFpbnRzIG9uIE5CUCwgTlBQLCBjU29pbCBhbmQgY1ZlZyBzdG9ja3MgYXQgdGhlIGVuZCBvZiB0aGUgMjB0aCBjZW50dXJ5LiBXZSB1c2UgMzAwIG9mIHRoZSA1MDAgbWVtYmVycywga2VlcGluZyBiYWNrIDIvNXRocyBmb3IgZW11bGF0b3IgdmFsaWRhdGlvbiBsYXRlci4KCldlIGFuc3dlciBzb21lIGJhc2ljIHF1ZXN0aW9ucy4KCldoYXQgcHJvcG9ydGlvbiBvZiB0aGUgbmV3IGVuc2VtYmxlIG1hdGNoIEFXJ3MgY29uc3RyYWludHM/CgpXaGF0IGRvIHRoZSB0aW1lc2VyaWVzIG9mIGNhcmJvbiBjeWNsZSBwcm9wZXJ0aWVzIGxvb2sgbGlrZSB3aXRoIGFuZCB3aXRob3V0IEFXJ3MgY29uc3RyYWludHM/CgpIb3cgZ29vZCBpcyBhIEdQIGVtdWxhdG9yPyBEb2VzIGl0IGdldCBiZXR0ZXIgb3ZlcmFsbCB3aXRoIHRoZSBuZXcgZW5zZW1ibGUgbWVtYmVycyBhZGRlZD8gSW4gcGFydGljdWxhciwgZG9lcyBpdCBnZXQgYmV0dGVyIGZvciB0aG9zZSBtZW1iZXJzIHdpdGhpbiB0aGUgQVcgY29uc3RyYWludHM/CgpEb2VzIGNvbXBhcmlzb24gb2YgdGhlIGVuc2VtYmxlIHdpdGggQXRtb3NwaGVyaWMgZ3Jvd3RoIG9ic2VydmF0aW9ucyBnaXZlIG1vcmUgb2YgYSBjb25zdHJhaW50PwoKVG8gZG86CgpEb2VzIHRoZSBzZW5zaXRpdml0eSBhbmFseXNpcyBjaGFuZ2U/CgojIFByZWxpbWluYXJpZXMKTG9hZCBsaWJyYXJpZXMsIGZ1bmN0aW9ucyBhbmQgZGF0YS4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyBMb2FkIGhlbHBlciBmdW5jdGlvbnMKCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcucGF0aCA9ICJmaWdzLyIsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5ncyA9IEZBTFNFKQoKIyBsb2FkIGhlbHBlciBmdW5jdGlvbnMsIGRhdGEgYW5kIGRvIHByZWxpbWluYXJ5IHByb2Nlc3Npbmcgb2YgdGhlIGVuc2VtYmxlLgpzb3VyY2UoJ0pVTEVTLUVTLTFwMC1jb21tb24uUicpCgpgYGAKCmBgYHtyfQoKZW5zbG9jIDwtICcvc2NyYXRjaC9oYWRhdy9qdWxlc19wb3N0cHJvY2Vzcy91LWNrMDA2LycKCmBgYAoKCmBgYHtyfQoKbWFrZUp1bGVzRW5zZW1ibGVNb2Rlcm5WYWx1ZSA8LSBmdW5jdGlvbihlbnNsb2MsIHZhcmxpc3QsIG5zdGFydCwgbmVuZCwgaXggPSAxNDQ6MTY0KXsKICAKICBuZW5zIDwtIChuZW5kIC0gbnN0YXJ0KSArIDEKICBkYXRtYXQgPC0gbWF0cml4KG5yb3cgPSBuZW5zLCBuY29sID0gbGVuZ3RoKHZhcmxpc3QpKQogIGNvbG5hbWVzKGRhdG1hdCkgPC0gdmFybGlzdAogIAogIGVuc2xpc3QgPC0gcGFzdGUoIlAiLCBmb3JtYXRDKG5zdGFydDpuZW5kLCB3aWR0aD00LCBmbGFnPSIwIiksIHNlcD0iIikKICAKICBmb3IoaSBpbiAxOm5lbnMpewogICAgCiAgICB2ZWMgPC0gcmVwKE5BLCBsZW5ndGgodmFybGlzdCkpCiAgICAKICAgIGVuc21lbWJlciA8LSBlbnNsaXN0W2ldIAogICAgCiAgICBmbiA8LSBwYXN0ZTAoZW5zbG9jLGVuc21lbWJlciwnL3N0YXRzLycsJ0pVTEVTLUVTLTFwMF8nLGVuc21lbWJlciwnX0FubnVhbF9nbG9iYWwubmMnKQogICAgCiAgICB0cnkobmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKSkKICAgIHRyeSh2ZWMgPC0gc2FwcGx5KHZhcmxpc3QsIEZVTiA9IG1vZGVyblZhbHVlLCBuYyA9IG5jLCBpeCA9IGl4KSkKICAgIGRhdG1hdFtpLCBdIDwtIHZlYwogICAgbmNfY2xvc2UobmMpCiAgfQogIHJldHVybihsaXN0KGRhdG1hdCA9IGRhdG1hdCwgZW5zbGlzdCA9IGVuc2xpc3QpKQp9CiAgCmBgYAoKCmBgYHtyfQpuc3RhcnQgPC0gNDk5Cm5lbmQgPC0gNzk4CgojaWYgKGZpbGUuZXhpc3RzKCJlbnNlbWJsZV93YXZlMDEucmRhdGEiKSkgewojICBsb2FkKCJlbnNlbWJsZV93YXZlMDEucmRhdGEiKQojfSBlbHNlIHsKICAKZW5zX3dhdmUwMV9tdiA8LSBtYWtlSnVsZXNFbnNlbWJsZU1vZGVyblZhbHVlKGVuc2xvYyA9ICcvc2NyYXRjaC9oYWRhdy9qdWxlc19wb3N0cHJvY2Vzcy91LWNrMDA2LycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFybGlzdCA9IHlfbmFtZXNfc3VtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gbnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVuZCA9IG5lbmQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXggPSAxNDQ6MTY0KSAKICAKIyAgc2F2ZShlbnNfd2F2ZTAxX212LCBmaWxlID0iZW5zZW1ibGVfd2F2ZTAxLnJkYXRhIikKI30KICAKYGBgCgpgYGB7cn0KCmxoc193YXZlMDEgPC0gcmVhZC50YWJsZSggJy4uL2NvbmZfZmlsZXNfYXVnbWVudF9KVUxFUy1FUy0xcDAvbGhzX2V4YW1wbGUudHh0JywgaGVhZGVyID0gVFJVRSkKClhfd2F2ZTAxID0gbm9ybWFsaXplKGxoc193YXZlMDEsIHdydCA9IHJiaW5kKGxoc19pLCBsaHNfaWksIGxoc193YXZlMDEpKQpjb2xuYW1lcyhYX3dhdmUwMSkgPSBjb2xuYW1lcyhsaHNfd2F2ZTAxKQoKIyBNYXRjaCB0aGUgMzAwIG91dHB1dHMgd2UncmUgdXNpbmcgaW4gdGhlIHRyYWluaW5nIGRhdGEKWF93YXZlMDFfdHJhaW4gPC0gWF93YXZlMDFbMTozMDAsXQoKYGBgCgpgYGB7cn0KWV9jb25zdF93YXZlMDEgPC0gZW5zX3dhdmUwMV9tdiRkYXRtYXRbLCB5bmFtZXNfY29uc3RdCgpZX2NvbnN0X3dhdmUwMV9zY2FsZWQgPC0gc3dlZXAoWV9jb25zdF93YXZlMDEsIDIsIFNUQVRTID0gc2NhbGV2ZWMsIEZVTiA9ICcvJyApCgoKYGBgCgojIyBIb3cgbWFueSBydW4gZmFpbHVyZXMgd2VyZSB0aGVyZT8gIAoKVGhlcmUgYXJlIG5vIE5BcyBidXQgc29tZSByZWxhdGl2ZSBodW1pZGl0eSB2YWx1ZXMgYXJlIGluZmluaXRlLgpUaGVyZSBhcmUgbm8gImxvdyBOUFAiIGVuc2VtYmxlIG1lbWJlcnMKCmBgYHtyfQoKbG93X25wcF9peF93YXZlMDEgPC0gd2hpY2goZW5zX3dhdmUwMV9tdiRkYXRtYXRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCAxZTUpCgptaW4oZW5zX3dhdmUwMV9tdiRkYXRtYXRbLCducHBfbmxpbV9sbmRfc3VtJ10pCgpZX3dhdmUwMV9ubGV2ZWwwX2l4IDwtIHdoaWNoKGlzLm5hKGVuc193YXZlMDFfbXYkZGF0bWF0WywnbmJwX2xuZF9zdW0nXSkpCgphbGwoaXMuZmluaXRlKGVuc193YXZlMDFfbXYkZGF0bWF0KSkKCndoaWNoKCFpcy5maW5pdGUoZW5zX3dhdmUwMV9tdiRkYXRtYXQpLCBhcnIuaW5kID0gVFJVRSkKCmVuc193YXZlMDFfbXYkZGF0bWF0W3doaWNoKCFpcy5maW5pdGUoZW5zX3dhdmUwMV9tdiRkYXRtYXQpLCBhcnIuaW5kID0gVFJVRSldCgpjb2xuYW1lcyhlbnNfd2F2ZTAxX212JGRhdG1hdClbOV0KCgpgYGAKCiMgRW5zZW1ibGUgYmVoYXZpb3VyIGluIGtleSAoY29uc3RyYWluaW5nKSBvdXRwdXRzLiAKCkdsb2JhbCBtZWFuIGZvciB0aGUgMjAgeWVhcnMgYXQgdGhlIGVuZCBvZiB0aGUgMjB0aCBDZW50dXJ5LiBUaGVyZSBpcyBzdGlsbCBhIHNpZ25pZmljYW50IGxvdyBiaWFzIG9uIGNWZWcgb3V0cHV0LgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQoKd2F2ZTAwY29sIDwtICdza3libHVlMicKd2F2ZTAxY29sIDwtICd0b21hdG8yJwoKd2F2ZTAwY29sIDwtICdkb2RnZXJibHVlMicKd2F2ZTAxY29sIDwtICdmaXJlYnJpY2snCnJhbmdlY29sIDwtICdncmV5JwoKCiMgSGlzdG9ncmFtIG9mIGxldmVsIDEgY29uc3RyYWludHMKaGNvbCA9ICdkYXJrZ3JleScKbGNvbCA9ICdibGFjaycKCiNwZGYoZmlsZSA9ICdmaWdzL2xldmVsXzJfY29uc3RyYWludHNfaGlzdHMucGRmJywgd2lkdGggPSA2LCBoZWlnaHQgPSA1KQpwYXIobWZyb3cgPSBjKDIsMiksIGZnID0gJ2RhcmtncmV5JywgbGFzID0gMSwgb21hID0gYygwLjEsIDAuMSwgNCwgMC4xKSkKCnRydW5jIDwtIGZ1bmN0aW9uKHgsIHZlYyl7CiAgCiAgZGF0IDwtIHhbeCA8IG1heCh2ZWMpICYgeCA+IG1pbih2ZWMpICBdCiAgCiAgZGF0CiAgCn0KCgpoIDwtIGhpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25icF9sbmRfc3VtJ10sIG1haW4gPSAnTkJQJywgeGxhYiA9ICdHdEMveWVhcicsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSkKaGlzdCh0cnVuYyhZX2NvbnN0X3dhdmUwMV9zY2FsZWQgWywnbmJwX2xuZF9zdW0nXSwgaCRicmVha3MpICwKICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDE1MCkgLCBicmVha3MgPSBoJGJyZWFrcywgYWRkID0gVFJVRSkKCnJ1ZyhZX2NvbnN0X3N0YW5fc2NhbGVkWyduYnBfbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYygwLCAxMDAsIDEwMCwgMCksIHkgPSBjKDAsIDAsIDEwMDAsIDEwMDApLAogICAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApLAogICAgICAgIGJvcmRlciA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCkpCgpoIDwtIGhpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSxjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIG1haW4gPSAnTlBQJywgeGxhYiA9ICdHdEMveWVhcicpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ25wcF9ubGltX2xuZF9zdW0nXSwgaCRicmVha3MpICwgCiAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCkgLCBicmVha3MgPSBoJGJyZWFrcywgYWRkID0gVFJVRSkKCnJ1ZyhZX2NvbnN0X3N0YW5fc2NhbGVkWyducHBfbmxpbV9sbmRfc3VtJ10sIGx3ZCA9IDIpCgpwb2x5Z29uKHggPSBjKDM1LCA4MCwgODAsIDM1KSwgeSA9IGMoMCwgMCwgMTAwMCwgMTAwMCksCiAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sLCA2MCksCiAgICAgICAgYm9yZGVyID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sKSkKCgpoIDwtIGhpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLCBtYWluID0gJ1NvaWwgQ2FyYm9uJywgeGxhYiA9ICdHdEMnKQpoaXN0KHRydW5jKFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCBbLCdjU29pbF9sbmRfc3VtJ10sIGgkYnJlYWtzKSAsIAogICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTUwKSAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ2NTb2lsX2xuZF9zdW0nXSwgbHdkID0gMikKCnBvbHlnb24oeCA9IGMoNzUwLCAzMDAwLCAzMDAwLCA3NTApLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgICBib3JkZXIgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKaCA8LSBoaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCdjVmVnX2xuZF9zdW0nXSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLCBtYWluID0gJ1ZlZ2V0YXRpb24gQ2FyYm9uJywgeGxhYiA9ICdHdEMnKQpoaXN0KHRydW5jKFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCBbLCdjVmVnX2xuZF9zdW0nXSwgaCRicmVha3MpICwgCiAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTUwKSAgLCBicmVha3MgPSBoJGJyZWFrcywgYWRkID0gVFJVRSkKCnJ1ZyhZX2NvbnN0X3N0YW5fc2NhbGVkWydjVmVnX2xuZF9zdW0nXSwgbHdkID0gMikKCnBvbHlnb24oeCA9IGMoMzAwLCA4MDAsIDgwMCwgMzAwKSwgeSA9IGMoMCwgMCwgMTAwMCwgMTAwMCksCiAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sLCA2MCksCiAgICAgICBib3JkZXIgPSAgbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sKSkKCiNkZXYub2ZmKCkKCnJlc2V0KCkKCmxlZ2VuZCgndG9wJywgaG9yaXogPSBUUlVFLCBmaWxsID0gYyhtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxNTApLCBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAxNTApLCBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSksIGxlZ2VuZCA9IGMoJ1dhdmUwMCcsICdXYXZlMDEnLCAnQVcgcmFuZ2UnKSkKYGBgCiMjIFdoYXQgcHJvcG9ydGlvbiBvZiBtb2RlbHMgKm5vdyogZmFsbCB3aXRoaW4gQW5keSdzIGNvbnN0cmFpbnRzPwoKQSB0aGlyZCEgQmV0dGVyIHRoYW4gYmVmb3JlLCBidXQgc3RpbGwgbm90IGdyZWF0LiBQb2ludGluZyBhdCBhIHNpZ25pZmljYW50IG1vZGVsIGRpc2NyZXBlbmN5IGluIGNWZWcKCmBgYHtyfQpBV19jb25zdHJhaW50cyA8LSBtYXRyaXgobnJvdyA9IDIsIG5jb2wgPSBsZW5ndGgoeW5hbWVzX2NvbnN0KSkKCkFXX2NvbnN0cmFpbnRzWzEsXSA8LSBjKDAsIDM1LCA3NTAsIDMwMCkKQVdfY29uc3RyYWludHNbMixdIDwtIGMoMTAwLCA4MCwgMzAwMCwgODAwKQoKY29sbmFtZXMoQVdfY29uc3RyYWludHMpIDwtIHluYW1lc19jb25zdApyb3duYW1lcyhBV19jb25zdHJhaW50cykgPC0gYygnbWluJywgJ21heCcpCgoKIyBjb25mb3JtIHRvIEFuZHkncyBiYXNpYyBjb25zdHJhaW50cwojbGV2ZWwyX2l4X3dhdmUwMSA8LSB3aGljaChhcHBseShZX2NvbnN0X3dhdmUwMV9zY2FsZWQsIDEsIEZVTiA9IHdpdGhpblJhbmdlLCBtYXhlcyAgPSBBV19jb25zdHJhaW50c1syLF0sIG1pbnMgPSBBV19jb25zdHJhaW50c1sxLF0gKSkKCiNubGV2ZWwyX2l4X3dhdmUwMSA8LSB3aGljaChhcHBseShZX2NvbnN0X3dhdmUwMV9zY2FsZWQsIDEsIEZVTiA9IHdpdGhpblJhbmdlLCBtYXhlcyAgPSBBV19jb25zdHJhaW50c1syLF0sIG1pbnMgPSBBV19jb25zdHJhaW50c1sxLF0gKSA9PSBGQUxTRSkKCmxldmVsMl9peF93YXZlMDEgPC0gd2hpY2goWV9jb25zdF93YXZlMDFfc2NhbGVkWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddIDwgODAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X3dhdmUwMV9zY2FsZWRbLCdjVmVnX2xuZF9zdW0nXSA+IDMwMCAmIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgICkKCgpgYGAKCk9mIHRoZSAzMDAgbWVtYmVycyBvZiB0aGUgd2F2ZTAxIGVuc2VtYmxlLCAxMDAgcGFzcyBBbmR5IFdpbHRzaGlyZSdzIExldmVsIDIgY29uc3RyYWludHMuCgpgYGB7cn0KCmxlbmd0aChsZXZlbDJfaXhfd2F2ZTAxKQoKYGBgCgpQYWlycyBwbG90IG9mIHRoZSBpbnB1dHMgdGhhdCBwYXNzIHRoZSBjb25zdHJhaW50cyB3aXRoIHJlc3BlY3QgdG8gdGhlIGxpbWl0cyBvZiB0aGUgb3JpZ2luYWwgZW5zZW1ibGUuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0KCnBhaXJzKFhfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgIHhsaW0gPSBjKDAsMSksCiAgICAgIHlsaW0gPSBjKDAsMSksCiAgICAgIGdhcCA9IDAsCiAgICAgIGxvd2VyLnBhbmVsID0gTlVMTCwKICAgICAgcGNoID0gMjAsCiAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMjAwKQogICAgICApCgpgYGAKCiMjIFRpbWVzZXJpZXMgb2YgbWVhbiBjYXJib24gY3ljbGUgcHJvcGVydGllcyBvdmVyIHdob2xlIHJ1bi4KCmBgYHtyfQoKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyBMb2FkIGVuc2VtYmxlCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmlmIChmaWxlLmV4aXN0cygiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiKSkgewogIGxvYWQoImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxLnJkYXRhIikKfSBlbHNlIHsKICAKICAjIHByaW1hcnkgY2FyYm9uIGN5Y2xlIG91dHB1dHMKICBucHBfZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAibnBwX25saW1fbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICBuYnBfZW5zIDwtICBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gIm5icF9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogIGNTb2lsX2VucyA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZSh2YXJpYWJsZSA9ICJjU29pbF9sbmRfc3VtIikgLyAxZTEyCiAgY1ZlZ19lbnMgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAiY1ZlZ19sbmRfc3VtIikgLyAxZTEyCiAgCiAgCiAgbGFpX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImxhaV9sbmRfbWVhbiIpCiAgCiAgIyBmbHV4ZXMKICByaF9sbmRfc3VtX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gInJoX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgZkx1Y19sbmRfc3VtX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImZMdWNfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICBmSGFydmVzdF9sbmRfc3VtX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImZIYXJ2ZXN0X2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgCiAgCiAgIyBmcmFjdGlvbnMKICB0cmVlRnJhY19sbmRfbWVhbl9lbnMgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZSh2YXJpYWJsZSA9ICJ0cmVlRnJhY19sbmRfbWVhbiIpCiAgc2hydWJGcmFjX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gInNocnViRnJhY19sbmRfbWVhbiIpCiAgYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImJhcmVzb2lsRnJhY19sbmRfbWVhbiIpCiAgI2MzUGZ0RnJhY19sbmRfbWVhbl9lbnMgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZSh2YXJpYWJsZSA9ICJjM1BmdEZyYWNfbG5kX21lYW5fZW5zIikKICAjYzRQZnRGcmFjX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImM0UGZ0RnJhY19sbmRfbWVhbl9lbnMiKQogIAogIHNhdmUobnBwX2VucywgbmJwX2VucywgY1NvaWxfZW5zLCBjVmVnX2VucywgbGFpX2xuZF9tZWFuX2VucywgcmhfbG5kX3N1bV9lbnMsIGZMdWNfbG5kX3N1bV9lbnMsIGZIYXJ2ZXN0X2xuZF9zdW1fZW5zLAogICAgICAgdHJlZUZyYWNfbG5kX21lYW5fZW5zLCBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zLCBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zLCBmaWxlID0gImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxLnJkYXRhIiApCiAgCn0KCnRvdGFsX2xhbmRfY2FyYm9uX2VucyA8LSBjU29pbF9lbnMgKyBjVmVnX2VucwoKYGBgCgpgYGB7cn0KCgptYWtlVGltZXNlcmllc0Vuc2VtYmxlIDwtIGZ1bmN0aW9uKGVuc2xvYywgdmFyaWFibGUsIG5zdGFydCwgbmVuZCwgbnRzID0gMTY0LCBjbiA9IDE4NTA6MjAxMyl7CiAgCiAgbmVucyA8LSAobmVuZCAtIG5zdGFydCkgKyAxCiAgIyBuZW5zIGlzIG51bWJlciBvZiBlbnNlbWJsZSBtZW1iZXJzCiAgIyBudHMgbGVuZ3RoIG9mIHRpbWVzZXJpZXMKICAjIGNuIGlzIGNvbG5hbWVzKCkKICBkYXRtYXQgPC0gbWF0cml4KE5BLCBucm93ID0gbmVucywgbmNvbCA9IG50cykKICBjb2xuYW1lcyhkYXRtYXQpIDwtIGNuCiAgCiAgZW5zbGlzdCA8LSBwYXN0ZSgiUCIsIGZvcm1hdEMobnN0YXJ0Om5lbmQsIHdpZHRoPTQsIGZsYWc9IjAiKSwgc2VwPSIiKQogICNmbG9jIDwtIHBhc3RlMChlbnNsb2MsZW5zbWVtYmVyLHN1YmRpcikKICAKICBmb3IoaSBpbiAxOm5lbnMpewogICAgCiAgICB2ZWMgPC0gcmVwKE5BLG50cykKICAgIAogICAgZW5zbWVtYmVyIDwtIGVuc2xpc3RbaV0gCiAgICAKICAgIGZuIDwtIHBhc3RlMChlbnNsb2MsZW5zbWVtYmVyLCcvc3RhdHMvJywnSlVMRVMtRVMtMXAwXycsZW5zbWVtYmVyLCdfQW5udWFsX2dsb2JhbC5uYycpCiAgICAKICAgIAogICAgdHJ5KG5jIDwtIG5jX29wZW4ocGFzdGUwKGZuKSkpCiAgICB0cnkoZGF0IDwtIGV4dHJhY3RUaW1lc2VyaWVzKG5jLCB2YXJpYWJsZSkpCiAgICAKICAgIGRhdG1hdFtpLCBdIDwtIGRhdAogICAgbmNfY2xvc2UobmMpCiAgfQogIGRhdG1hdAp9CgpgYGAKCgpgYGB7cn0KCm5zdGFydCA8LSA0OTkKbmVuZCA8LSA3OTgKCmlmIChmaWxlLmV4aXN0cygiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiKSkgewogIGxvYWQoImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxLnJkYXRhIikKfSBlbHNlIHsKICAKICAjIHByaW1hcnkgY2FyYm9uIGN5Y2xlIG91dHB1dHMKICAgbnBwX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgdmFyaWFibGUgPSAibnBwX25saW1fbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAgbmJwX2Vuc193YXZlMDEgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsdmFyaWFibGUgPSAibmJwX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIGNTb2lsX2Vuc193YXZlMDEgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsdmFyaWFibGUgPSAiY1NvaWxfbG5kX3N1bSIpIC8gMWUxMgogICBjVmVnX2Vuc193YXZlMDEgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsdmFyaWFibGUgPSAiY1ZlZ19sbmRfc3VtIikgLyAxZTEyCiAgIyAKICAjIAogICBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLHZhcmlhYmxlID0gImxhaV9sbmRfbWVhbiIpCiAgIyAKICAjICMgZmx1eGVzCiAgIHJoX2xuZF9zdW1fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCB2YXJpYWJsZSA9ICJyaF9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogICBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCB2YXJpYWJsZSA9ICJmTHVjX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCB2YXJpYWJsZSA9ICJmSGFydmVzdF9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogICMgCiAgIyAKICAjICMgZnJhY3Rpb25zCiAgIHRyZWVGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgIHZhcmlhYmxlID0gInRyZWVGcmFjX2xuZF9tZWFuIikKICAgc2hydWJGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgIHZhcmlhYmxlID0gInNocnViRnJhY19sbmRfbWVhbiIpCiAgIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsICB2YXJpYWJsZSA9ICJiYXJlc29pbEZyYWNfbG5kX21lYW4iKQogIAogICBzYXZlKG5wcF9lbnNfd2F2ZTAxLAogICAgICAgIG5icF9lbnNfd2F2ZTAxLAogICAgICAgIGNTb2lsX2Vuc193YXZlMDEsCiAgICAgICAgY1ZlZ19lbnNfd2F2ZTAxLAogICAgICAgIGxhaV9sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgIHJoX2xuZF9zdW1fZW5zX3dhdmUwMSwKICAgICAgICBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMSwKICAgICAgICBmSGFydmVzdF9sbmRfc3VtX2Vuc193YXZlMDEsCiAgICAgICAgdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgICBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgICBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgIGZpbGUgPSAiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiICkKICAgCn0KCiN0b3RhbF9sYW5kX2NhcmJvbl9lbnNfd2F2ZTAxIDwtIGNTb2lsX2Vuc193YXZlMDEgKyBjVmVnX2Vuc193YXZlMDEKCmBgYAoKCgpgYGB7ciBwbG90LWNhcmJvbi1jeWNsZS10aW1lc2VyaWVzLXByaW1hcnksIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTJ9CgpsY29sX3dhdmUwIDwtIG1ha2VUcmFuc3BhcmVudCgnZG9kZ2VyYmx1ZTInLCAgMTIwKQpsY29sX3dhdmUwMSA8LSBtYWtlVHJhbnNwYXJlbnQoJ2ZpcmVicmljaycsICAxMjApCmxjb2xfd2F2ZTAxX2xldmVsMiA8LSAnZ29sZCcKc3RhbmNvbCA9ICdibGFjaycKCmxpbmVQbG90TXVsdGlFbnMgPC0gZnVuY3Rpb24oeWVhcnMsIGVuczEsIGVuczIsIGVuczMsIGNvbDEsIGNvbDIsIGNvbDMsIHlsYWIsIG1haW4sIHlsaW0gPSBOVUxMKXsKICAjIFBsb3Qgd2F2ZTAwIGFuZCB3YXZlMDEgdGltZXNlcmllcyBvbiB0b3Agb2Ygb25lIGFub3RoZXIKICAKICBudCA8LSBsZW5ndGgoeWVhcnMpIAogIGlmKGlzLm51bGwoeWxpbSkpewogICAgCiAgeWxpbSA9IHJhbmdlKGMoZW5zMVssMV0sIGVuczFbLG50XSwgZW5zMlssMV0sIGVuczJbICxudF0sIGVuczNbLDFdLCBlbnMzWywgbnRdKSkKICB9CiAgCiAgZWxzZSB5bGltIDwtIHlsaW0KICAKICBtYXRwbG90KHllYXJzLCB0KGVuczEpLCB0eXBlID0gJ2wnLCBsdHkgPSAnc29saWQnLHlsaW0gPSB5bGltLCBjb2wgPSBjb2wxLAogICAgICAgIHlsYWIgPSB5bGFiLCBtYWluID0gbWFpbiwgeGxhYiA9ICcnLAogICAgICAgIGJ0eSA9ICduJykKICBtYXRsaW5lcyh5ZWFycywgdChlbnMyKSwgY29sID0gY29sMiwgbHR5ID0gJ3NvbGlkJykKICAgIG1hdGxpbmVzKHllYXJzLCB0KGVuczMpLCBjb2wgPSBjb2wzLCBsdHkgPSAnc29saWQnKQp9CgoKcGFyKG1mcm93PSBjKDMsNCksIGxhcyA9IDEpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBucHBfZW5zLCBlbnMyID0gbnBwX2Vuc193YXZlMDEsIGVuczMgPSBucHBfZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ05QUCcpCgpsaW5lcyh5ZWFycyxucHBfc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9ICBuYnBfZW5zLCBlbnMyID0gbmJwX2Vuc193YXZlMDEsIGVuczMgPSBuYnBfZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTkJQJywgeWxpbSA9IGMoLTEwLDEwKSkKCmxpbmVzKHllYXJzLCBuYnBfc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGNTb2lsX2VucywgZW5zMiA9IGNTb2lsX2Vuc193YXZlMDEsIGVuczMgPSBjU29pbF9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnY1NvaWwnLCB5bGltID0gcmFuZ2UoYyhjU29pbF9lbnNbLDFdLCBjU29pbF9lbnNbLDE2NF0pKSkKCmxpbmVzKHllYXJzLCBjU29pbF9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gY1ZlZ19lbnMsIGVuczIgPSBjVmVnX2Vuc193YXZlMDEsIGVuczMgPSBjVmVnX2Vuc193YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjVmVnJykKCmxpbmVzKHllYXJzLCBjVmVnX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBsYWlfbG5kX21lYW5fZW5zLCBlbnMyID0gbGFpX2xuZF9tZWFuX2Vuc193YXZlMDEsIGVuczMgPSBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ0xhaScpCgpsaW5lcyh5ZWFycywgbGFpX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSByaF9sbmRfc3VtX2VucywgZW5zMiA9IHJoX2xuZF9zdW1fZW5zX3dhdmUwMSwgZW5zMyA9IHJoX2xuZF9zdW1fZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsICBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdSSCcpCgpsaW5lcyh5ZWFycywgcmhfbG5kX3N1bV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gZkx1Y19sbmRfc3VtX2VucywgZW5zMiA9IGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxLCBlbnMzID0gZkx1Y19sbmRfc3VtX2Vuc193YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdmTHVjJykKCmxpbmVzKHllYXJzLCBmTHVjX2xuZF9zdW1fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGZIYXJ2ZXN0X2xuZF9zdW1fZW5zLCBlbnMyID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfd2F2ZTAxLAogICAgICAgICAgICAgICAgIGVuczMgPSBmSGFydmVzdF9sbmRfc3VtX2Vuc193YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdmSGFydmVzdCcpCgpsaW5lcyh5ZWFycywgZkhhcnZlc3RfbG5kX3N1bV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zLCBlbnMyID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgICAgICAgICAgICBlbnMzID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ3RyZWVmcmFjJwogICAgICAgICAgICAgICAgICkKCmxpbmVzKHllYXJzLCB0cmVlRnJhY19sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gc2hydWJGcmFjX2xuZF9tZWFuX2VucywgZW5zMiA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgICAgICAgICAgIGVuczMgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnc2hydWJmcmFjJwopCgpsaW5lcyh5ZWFycywgc2hydWJGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zLCBlbnMyID0gYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgICAgICAgICAgZW5zMyA9IGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnYmFyZXNvaWxmcmFjJykKCmxpbmVzKHllYXJzLCBiYXJlc29pbEZyYWNfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCnJlc2V0KCkKCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCd3YXZlMDEnLCd3YXZlMDEgbGV2ZWwyJywnc3RhbmRhcmQnKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41LCBjb2wgPSBjKGxjb2xfd2F2ZTAsIGxjb2xfd2F2ZTAxLCBsY29sX3dhdmUwMV9sZXZlbDIsIHN0YW5jb2wpLCBpbnNldCA9IGMoMC4wNSwgMC4xNSkgKQoKCmBgYApUaGlzIGlzIGEgcGxvdCBvZiB0aW1lc2VyaWVzIG9mIFdhdmUwMCwgV2F2ZTAxLCBhbmQgbGV2ZWwyLWNvbnN0cmFpbmVkIHdhdmUwMSBvbiB0b3Agb2Ygb25lIGFub3RoZXIuIFdlIHNlZSB0aGF0IHRoZSB3YXZlMDEgaXMgY2xvc2VyIHRvIHRoZSBzdGFuZGFyZCB0aGFuIHdhdmUwMCwgYW5kIHRoZSBsZXZlbC0yIGNvbnN0cmFpbmVkIHdhdmUwMSBlbnNlbWJsZSBpcyBvZnRlbiBjbG9zZXIgYWdhaW4uIEhvd2V2ZXIsIHRoZXJlIGFyZSBzdGlsbCBxdWl0ZSBsYXJnZSBkaXNjcmVwYW5jaWVzLiBGb3IgZXhhbXBsZSwgYmFyZXNvaWxmcmFjIGlzIG9mdGVuIHdheSB0b28gaGlnaCwgc2hydWJmcmFjIGlzIG9mdGVuIHRvbyBsb3cgKHRob3VnaCBib3RoIHRoZXNlIHNwYW4gdGhlIHN0YW5kYXJkKS4gVHJlZWZyYWMgaXMgYXdheSBmcm9tIHplcm8sIGJ1dCBzdGlsbCBvZnRlbiB0b28gbG93IG9yIHRvbyBoaWdoLiBXaGlsZSBmSGFydmVzdCBsb29rcyBnb29kLCBmTHVjIGRvZXMgbm90IGFwcGVhciBjb25zdHJhaW5lZCBieSB0aGUgcHJvY2VzcyBhdCBhbGwuIFJIIChzb2lsIHJlc3BpcmF0aW9uKSBsb29rcyB3ZWxsIGNvbnN0cmFpbmVkLCB3aGVyZWFzIGxhaSBpcyBvZnRlbiB0b28gbG93LiAgCgpPbmUgdGhpbmcgd2UgY291bGQgZG8gbmV4dCBpcyBjb25zdHJhaW4gaW5wdXQgc3BhY2UgYWdhaW4sIHVzaW5nIG9ic2VydmF0aW9ucyBvciAidG9sZXJhbmNlIHRvIGVycm9yIiBvbiBzb21lIG9yIGFsbCBvZiB0aGVzZSBvdXRwdXRzLiAgCgpXZSBjb3VsZCBhbHNvIGV4dGVuZCBzZW5zaXRpdml0eSBhbmFseXNpcyB0byB3b3JrIG91dCB3aGF0IGNvbnRyb2xzIGUuZy4gdHJlZWZyYWMuCgoKIyBFbXVsYXRvciBmaXRzCgpXZSBob3BlIHRoYXQgcnVubmluZyB0aGUgbmV3IGVuc2VtYmxlIGdpdmVzIHVzIGEgYmV0dGVyIGVtdWxhdG9yLCBhbmQgYWxsb3dzIHVzIHRvIHJ1bGUgb3V0IG1vcmUgaW5wdXQgc3BhY2UuCldlIHBhcnRpY3VsYXJseSBob3BlIHRoYXQgdGhlIGVtdWxhdG9yIGlzIGJldHRlciBmb3IgdGhvc2UgbWVtYmVycyB0aGF0IGFyZSBpbnNpZGUgQVcncyBjb25zdHJhaW50cy4KCkZpcnN0LCB3ZSBjYW4gbG9vayBhdCB0aGUgZW11bGF0b3IgZXJyb3JzIGluIHR3byBjYXNlczogVGhlIGxldmVsMWEgZGF0YSAoYSBiYXNpYyBjYXJib24gY3ljbGUpLCBhbmQgdGhlbiB3aXRoIHRoZSBXYXZlMDEgZGF0YSwgd2hpY2ggc2hvdWxkIGhhdmUgc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3MuIChXZSBzaG91bGQgaGF2ZSBlbGltaW5hdGVkIHJlYWxseSBiYWQgc2ltdWxhdGlvbnMsIGJ1dCB3YXZlMDEgaXMgbm90IGNvbnN0cmFpbmVkIHRoZSBkYXRhIHBlcmZlY3RseSB0byBiZSB3aXRoaW4gQVcgY29uc3RyYWludHMuKQoKCmBgYHtyfQojIyBFbXVsYXRvciBmaXQgbGlzdCBvZiBsZXZlbCAxYSBlbnNlbWJsZQoKI2ZpdF9saXN0X2NvbnN0X2xldmVsMWEgPC0gY3JlYXRlS21GaXRMaXN0KFggPSBYX2xldmVsMWEsIFkgPSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkKQoKWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9saXN0IDwtIG1hdDJsaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpCgpmaXRfbGlzdF9jb25zdF9sZXZlbDFhIDwtIG1jbGFwcGx5KFggPSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2xpc3QsIEZVTiA9IGttLCBmb3JtdWxhID0gfi4sIGRlc2lnbiA9IFhfbGV2ZWwxYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYy5jb3JlcyA9IDQsIGNvbnRyb2wgPSBsaXN0KHRyYWNlID0gRkFMU0UpKQoKCgoKYGBgCgoKCmBgYHtyfQojIyBSZW1vdmUgYW4gb3V0bGllciBmcm9tIHRoZSBuZXcgd2F2ZSBhbmQgYnVpbGQgZW11bGF0b3JzCiMgQmluZCB0b2dldGhlciB0aGUgaW5wdXQgbWF0cmljZXMgYW5kIHNjYWxlZCBvdXRwdXQgZGF0YQpYX2xldmVsMWFfd2F2ZTAxIDwtIHJiaW5kKFhfbGV2ZWwxYSwgWF93YXZlMDFfdHJhaW4pWy00NDAsIF0KCllfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkIDwtIHJiaW5kKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQsIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZClbLTQ0MCwgXQoKCmFwcGx5KFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkICwyLCB3aGljaC5tYXgpCmFwcGx5KFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkICwyLCB3aGljaC5taW4pCmBgYAoKCkZvdW5kIHRoZSBvdXRsaWVyIC0gbG9va3MgbGlrZSBpdCdzIDQ0MApgYGB7cn0KCmZpbmRPdXRsaWVycyA8LSBmdW5jdGlvbih4LCBzZHMgPSA2KXsKICAKICBpeCA8LSB3aGljaChhYnMoeCAtIG1lYW4oeCkpID4gKHNkcyAqIHNkKHgpKSkKICAKfQoKYXBwbHkoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgLDIsIGZpbmRPdXRsaWVycywgc2RzID0gMTApCmBgYAoKCmBgYHtyfQojIENyZWF0ZSBmaXQgbGlzdHMgZm9yIHRoZSBjb21iaW5lZCBkYXRhCllfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkX2xpc3QgPC0gbWF0Mmxpc3QoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQpCgpmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbGlzdCAsIEZVTiA9IGttLCBmb3JtdWxhID0gfi4sIGRlc2lnbiA9IFhfbGV2ZWwxYV93YXZlMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWMuY29yZXMgPSA0LCBjb250cm9sID0gbGlzdCh0cmFjZSA9IEZBTFNFKSkKCgpgYGAKCiMjIExlYXZlLW9uZS1vdXQgYW5hbHlzZXMgb2YgZW11bGF0b3IgcHJlZGljdGlvbiBhY2N1cmFjeQoKYGBge3J9Cgpsb29saXN0X2ttX1lfbGV2ZWwxYSA8LSBtY2xhcHBseShYID0gZml0X2xpc3RfY29uc3RfbGV2ZWwxYSwgRlVOID0gbGVhdmVPbmVPdXQua20sIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCmxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gZml0X2xpc3RfY29uc3RfbGV2ZWwxYV93YXZlMDEsIEZVTiA9IGxlYXZlT25lT3V0LmttLCB0eXBlID0gJ1VLJywgdHJlbmQucmVlc3RpbSA9IFRSVUUpCgpgYGAKCgpgYGB7cn0KCmxvb3N0YXRzX2ttX1lfbGV2ZWwxYSA8LSBsYXBwbHkoZml0X2xpc3RfY29uc3RfbGV2ZWwxYSwgRlVOID0ga21Mb29TdGF0cykKbG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMSA8LSBsYXBwbHkoZml0X2xpc3RfY29uc3RfbGV2ZWwxYV93YXZlMDEsIEZVTiA9IGttTG9vU3RhdHMpCgpgYGAKCgpUaGUgdG9wIHJvdyBzaG93cyB0aGUgbGVhdmUtb25lLW91dCBwcmVkaWN0aW9uIGFjY3VyYWN5IG9mIHRoZSBvcmlnaW5hbCB3YXZlMDAgZW5zZW1ibGUsIGFuZCB0aGUgbG93ZXIgcm93IHRoZSBlbnRpcmUgd2F2ZTAwIEFORCB3YXZlMDEgZW5zZW1ibGUgY29tYmluZWQuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoMiw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW4gLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDEwMCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCA1MCkpCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDFhIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCiNkZXYub2ZmKCkKCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbiAtICgyKmxvbyRzZCksIGxvbyRtZWFuICsgKDIqbG9vJHNkKSkgKQogIHBsb3QoeSwgbG9vJG1lYW4sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCB5bGltID0geWxpbSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMTAwKSwKICAgICAgIHBjaCA9IDE5KQogIHNlZ21lbnRzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDEwMCkpCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCm10ZXh0KCdBY3R1YWwnLCBzaWRlID0gMSwgbGluZSA9IDEsIG91dGVyID0gVFJVRSwgY2V4ID0gMiApCm10ZXh0KCdQcmVkaWN0ZWQnLCBzaWRlID0gMiwgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCm10ZXh0KCdMZXZlbCAxYSBlbnNlbWJsZSBvdXRwdXRzJywgc2lkZSA9IDMsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpCgpgYGAKCmBgYHtyfQojIHJlbW92ZSB0byBsZXZlbCAxYSBSZWxhdGl2ZSB0byB0b3BsZXZlbF9peCAtIHVzZWZ1bCBmb3IgcGxvdHRpbmcgZXRjLgojdG9wbGV2ZWxfdG9fbGV2ZWxfMWFfaXggPC0odG9wbGV2ZWxfaXhbLVlfbmxldmVsMF9peF0pW2xldmVsMWFfaXhdCgojIFNvIGZ1cnRoZXIgY29uc3RyYWluaW5nIHRvIGxldmVsIDIgY2FuIGJlIGFzc29jaWF0ZWQgYmFjayB0byB0aGUgdG9wIGxldmVsLgoKbGV2ZWwyX2l4IDwtIHdoaWNoKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCduYnBfbG5kX3N1bSddID4gMCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgIAogICkKCmxldmVsMl9peF9sZXZlbDFhX3dhdmUwMSA8LSB3aGljaChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ25icF9sbmRfc3VtJ10gPiAwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCdjU29pbF9sbmRfc3VtJ10gPiA3NTAgJiBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCdjVmVnX2xuZF9zdW0nXSA8IDgwMAogICAgICAgICAgICAgICAgICApCgpgYGAKCiMjIEVtdWxhdG9yIGFjY3VyYWN5IG9mIG1lbWJlcnMgZnJvbSB3YXZlIDAwIGFuZCB3YXZlIDAxIHRoYXQgcGFzcyBsZXZlbCAyIChBVydzKSBjb25zdHJhaW50cwoKV2Ugc2VlIHRoYXQgdGhlIGVycm9yIHN0YXRzIGZvciBzb21lIG9mIHRoZSBvdXRwdXRzIGZyb20gd2F2ZTAxIGFyZSB3b3JzZSwgYnV0IHRoZXJlIGFyZSBtYW55IG1vcmUgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGxpZSB3aXRoaW4gdGhlIGNvbnN0cmFpbnRzIGZvciB3YXZlIDAxLgoKInBtYWUiIGlzICJwcm9wb3J0aW9uYWwgbWVhbiBhYnNvbHVlIGVycm9yIiwgd2hpY2ggaXMgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgZXhwcmVzc2VkIGFzIGEgcGVyY2VudGFnZSBvZiB0aGUgb3JpZ2luYWwgKG1pbmltYWxseSBjb25zdHJhaW5lZCkgZW5zZW1ibGUgcmFuZ2UgaW4gdGhhdCBvdXRwdXQuIAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpwYXIobWZyb3cgPSBjKDIsNCksIG1hciA9IGMoMyw0LDIsMiksIG9tYSA9IGMoNCw0LDQsMC4xKSkKZm9yKGkgaW4gMTpsZW5ndGgobG9vbGlzdF9rbV9ZX2xldmVsMWEpKXsKICAKICB5IDwtIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbbGV2ZWwyX2l4LCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYVtbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuW2xldmVsMl9peF0gLSAoMipsb28kc2RbbGV2ZWwyX2l4XSksIGxvbyRtZWFuW2xldmVsMl9peF0gKyAoMipsb28kc2RbbGV2ZWwyX2l4XSkpICkKICBwbG90KHksIGxvbyRtZWFuW2xldmVsMl9peF0sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCB5bGltID0geWxpbSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTAwKSwKICAgICAgIHBjaCA9IDE5KQogIHNlZ21lbnRzKHgwID0geSwgeTAgPSBsb28kbWVhbltsZXZlbDJfaXhdIC0gKDIqbG9vJHNkW2xldmVsMl9peF0pICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW5bbGV2ZWwyX2l4XSArICgyKmxvbyRzZFtsZXZlbDJfaXhdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTAwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFbW2ldXSRwbWFlLDIpLCclJykgLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIpCgp9CgojZGV2Lm9mZigpCgojcGRmKGZpbGUgPSAnZmlncy9rbWxvb3N0YXRzX1lfbGV2ZWwxYS5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKZm9yKGkgaW4gMTpsZW5ndGgobG9vbGlzdF9rbV9ZX2xldmVsMWEpKXsKICAKICB5IDwtIFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMSwgaV0KICBsb28gPC0gbG9vbGlzdF9rbV9ZX2xldmVsMWFfd2F2ZTAxW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW5bbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSAtICgyKmxvbyRzZFtsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdKSwgbG9vJG1lYW5bbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSArICgyKmxvbyRzZFtsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdKSkgKQogIHBsb3QoeSwgbG9vJG1lYW5bbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAxMDApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0gLSAoMipsb28kc2RbbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdICsgKDIqbG9vJHNkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCA1MCkpCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCm10ZXh0KCdBY3R1YWwnLCBzaWRlID0gMSwgbGluZSA9IDEsIG91dGVyID0gVFJVRSwgY2V4ID0gMiApCm10ZXh0KCdQcmVkaWN0ZWQnLCBzaWRlID0gMiwgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCm10ZXh0KCdMZXZlbCAyIGNvbnN0cmFpbmVkIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCmBgYAoKIyMgRG9lcyB0aGUgZW11bGF0b3IgaW1wcm92ZSBpcyB5b3UgbG9vayBhdCBvbmx5IHRoZSAzNyBtZW1iZXJzIHRoYXQgcGFzcyBsZXZlbCAyIGNvbnN0cmFpbnRzIGluIHdhdmUgMDA/ClRoaXMgZ2l2ZXMgdXMgYW4gaWRlYSBvZiBob3cgZ29vZCB0aGUgZW11bGF0b3IgaXMgd2hlcmUgaXQgcmVhbGx5IG1hdHRlcnMsIGFuZCBhcyB0aGUgbWVtYmVycyBhcmUgY29uc2lzdGVudCwgZ2l2ZXMgdXMgYSBmYWlyZXIgaWRlYSBvZiB3aGV0aGVyIHRoZSBlbXVsYXRvcnMgaGF2ZSBpbXByb3ZlZCB3aXRoIG1vcmUgbWVtYmVycy4KCkdvb2QgbmV3cyBpcywgdGhlIGVtdWxhdG9ycyBhcmUgbW9yZSBhY2N1cmF0ZSBmb3Igd2F2ZTAxLgoKYGBge3J9CgoKa21Mb29TdGF0c1N1YnNldCA8LSBmdW5jdGlvbiAoa20sIGl4LCB0eXBlID0gIlVLIikgCnsKICAjIENhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIGEgc3Vic2V0IG9mIHRoZSBtZW1iZXJzIG9mIGEga20gZml0IGxpc3QKICAgIGxvbyA8LSBsZWF2ZU9uZU91dC5rbShrbSwgdHlwZSA9IHR5cGUsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQogICAgcHJlZGRpZmYgPC0gbG9vJG1lYW5baXhdIC0ga21AeVtpeF0KICAgIG1hZSA8LSBtZWFuKGFicyhwcmVkZGlmZikpCiAgICBybXNlIDwtIHNxcnQobWVhbihwcmVkZGlmZl4yKSkKICAgIG1heGVyciA8LSBtYXgocHJlZGRpZmYpCiAgICBhYnNkaWZmIDwtIGFicyhkaWZmKHJhbmdlKGttQHkpKSkKICAgIHBtYWUgPC0gKG1hZS9hYnNkaWZmKSAqIDEwMAogICAgcmV0dXJuKGxpc3QobG9vID0gbG9vLCBtYWUgPSBtYWUsIHBtYWUgPSBwbWFlLCBtYXhlcnIgPSBtYXhlcnIpKQp9CgoKYGBgCgoKYGBge3J9Cgpsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDIgPC0gcmFwcGx5KGxvb2xpc3Rfa21fWV9sZXZlbDFhLCBmID0gZnVuY3Rpb24oeCkgeFtsZXZlbDJfaXhdLCBob3cgPSAibGlzdCIpCgpsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFfbGV2ZWwyIDwtIHJhcHBseShsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDEsIGYgPSBmdW5jdGlvbih4KSB4W2xldmVsMl9peF0sIGhvdyA9ICJsaXN0IikKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoMiwyKSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDIpKXsKICAKICB5IDwtIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbbGV2ZWwyX2l4LCBpXQogIAogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDJbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbi0gKDIqbG9vJHNkKSwgbG9vJG1lYW4gKyAoMipsb28kc2QpKSApCiAgcGxvdCh5LCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAyNTApLAogICAgICAgcGNoID0gMTkpCiAgYXJyb3dzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDE1MCkgLCAgYW5nbGUgPSA5MCwgY29kZSA9IDMsIGxlbmd0aCA9IDAuMDMpCiAgCiAgeTEgPC0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbbGV2ZWwyX2l4LCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFfbGV2ZWwyW1tpXV0KICAKICAgIHBvaW50cyh5MSwgbG9vJG1lYW4sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCB5bGltID0geWxpbSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMjUwKSwKICAgICAgIHBjaCA9IDE5KQogIGFycm93cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAyNTApLCAgYW5nbGUgPSA5MCwgY29kZSA9IDMsIGxlbmd0aCA9IDAuMDMpCiAgCiAgCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDIgd2F2ZSAwMCBlbnNlbWJsZSBvdXRwdXRzJywgc2lkZSA9IDMsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpCgpyZXNldCgpCmxlZ2VuZCgndG9wbGVmdCcsIHBjaCA9IDE5LCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wgKSwgaG9yaXogPSBUUlVFKQoKCmBgYAoKVGhlc2UgbGVhdmUtb25lLW91dCBwcmVkaWN0aW9uIGFjY3VyYWN5IHBsb3RzIHJhbmsgdGhlIGVuc2VtYmxlIG1lbWJlcnMgZnJvbSBsYXJnZXN0IHVuZGVycHJlZGljdGlvbiB0byBsYXJnZXN0IG92ZXJwcmVkaWN0aW9uIHVzaW5nIHRoZSB3YXZlMDAgcHJlZGljdGlvbnMuIEEgcGVyZmVjdCBwcmVkaWN0aW9uIHdvdWxkIGFwcGVhciBvbiB0aGUgaG9yaXpvbnRhbCAiemVybyIgbGluZS4KCk1hbnkgb2YgdGhlIHdhdmUwMSBwcmVkaWN0aW9ucyBhcmUgY2xvc2VyIHRvIHRoZSBob3Jpem9udGFsIGxpbmUsIGFuZCB0aGVyZWZvcmUgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucy4gIAoKTm9uZSBvZiB0aGUgcHJlZGljdGlvbnMgYXJlIG91dHNpZGUgdGhlIHVuY2VydGFpbnR5IGJvdW5kcywgd2hpY2ggc3VnZ2VzdHMgdGhleSBhcmUgb3ZlcmNvbnNlcnZhdGl2ZSAoc2hvdWxkIGJlIHNtYWxsZXIpLgoKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoNCwxKSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDIpKXsKICAKICB5IDwtIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbbGV2ZWwyX2l4LCBpXQoKICBsb29fMDAgPC0gbG9vbGlzdF9rbV9ZX2xldmVsMWFfbGV2ZWwyW1tpXV0KICBsb29fMDEgPC0gbG9vbGlzdF9rbV9ZX2xldmVsMWFfd2F2ZTAxX2xldmVsMltbaV1dCiAgCiAgcHJlZGRpZmZfd2F2ZTAwIDwtIHkgLSBsb29fMDAkbWVhbgogIHByZWRkaWZmX3dhdmUwMSA8LSB5IC0gbG9vXzAxJG1lYW4KICAKICAgICMgcmFuayBieSB0aGUgb3JpZ2luYWwgd2F2ZSAwMCBwcmVkaWN0aW9ucwogIGxvb19yYW5rX2l4IDwtIHNvcnQocHJlZGRpZmZfd2F2ZTAwICwgaW5kZXgucmV0dXJuID0gVFJVRSkKICAKICAgeWxpbSA8LSByYW5nZShjKHByZWRkaWZmX3dhdmUwMFtsb29fcmFua19peCRpeF0gLSAoMipsb29fMDAkc2RbbG9vX3JhbmtfaXgkaXhdKSwKICAgICAgICAgICAgICAgICAgIHByZWRkaWZmX3dhdmUwMFtsb29fcmFua19peCRpeF0gKyAoMipsb29fMDAkc2RbbG9vX3JhbmtfaXgkaXhdKSwKICAgICAgICAgICAgICAgICAgIHByZWRkaWZmX3dhdmUwMVtsb29fcmFua19peCRpeF0gLSAoMipsb29fMDEkc2RbbG9vX3JhbmtfaXgkaXhdKSwKICAgICAgICAgICAgICAgICAgIHByZWRkaWZmX3dhdmUwMVtsb29fcmFua19peCRpeF0gKyAoMipsb29fMDEkc2RbbG9vX3JhbmtfaXgkaXhdKQogICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICkKICAgCiAgIHBsb3QocHJlZGRpZmZfd2F2ZTAwW2xvb19yYW5rX2l4JGl4XSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDI1NSksCiAgICAgICAgcGNoID0gMTksIHlsaW0gPSB5bGltKQogICAKICAgYWJsaW5lKGggPSAwKQogICAKICBhcnJvd3MoeDAgPSAxOmxlbmd0aCh5KSwgeTAgPSBwcmVkZGlmZl93YXZlMDBbbG9vX3JhbmtfaXgkaXhdIC0gKDIqbG9vXzAwJHNkW2xvb19yYW5rX2l4JGl4XSkgICwgeDEgPSAxOmxlbmd0aCh5KSAsIHkxID0gcHJlZGRpZmZfd2F2ZTAwW2xvb19yYW5rX2l4JGl4XSArICgyKmxvb18wMCRzZFtsb29fcmFua19peCRpeF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxNTApLCAgYW5nbGUgPSA5MCwgY29kZSA9IDMsIGxlbmd0aCA9IDAuMDMpCiAgIAogIHBvaW50cyhwcmVkZGlmZl93YXZlMDFbbG9vX3JhbmtfaXgkaXhdLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMjU1KSwKICAgICAgICBwY2ggPSAxOSkKICAKICAgIGFycm93cyh4MCA9IDE6bGVuZ3RoKHkpLCB5MCA9IHByZWRkaWZmX3dhdmUwMVtsb29fcmFua19peCRpeF0gLSAoMipsb29fMDEkc2RbbG9vX3JhbmtfaXgkaXhdKSAgLCB4MSA9IDE6bGVuZ3RoKHkpICwgeTEgPSBwcmVkZGlmZl93YXZlMDFbbG9vX3JhbmtfaXgkaXhdICsgKDIqbG9vXzAxJHNkW2xvb19yYW5rX2l4JGl4XSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDE1MCksIGFuZ2xlID0gOTAsIGNvZGUgPSAzLCBsZW5ndGggPSAwLjAzKQogICAKICAgbXRleHQoY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZClbaV0sIHNpZGUgPSAzLCBhZGogPSAwLCBsaW5lID0gMSkKICAKCn0KCgogcmVzZXQoKQogbGVnZW5kKCd0b3BsZWZ0JywgcGNoID0gMTksIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsICd3YXZlMDEnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCApLCBob3JpeiA9IFRSVUUpCgpgYGAKCgpgYGB7cn0KCmxvb3N0YXRzX2ttX1lfbGV2ZWwxYV9zdWIgPC0gbGFwcGx5KGZpdF9saXN0X2NvbnN0X2xldmVsMWEsIEZVTiA9IGttTG9vU3RhdHNTdWJzZXQsIGl4ID0gbGV2ZWwyX2l4KQpsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxX3N1YiA8LSBsYXBwbHkoZml0X2xpc3RfY29uc3RfbGV2ZWwxYV93YXZlMDEsIEZVTiA9IGttTG9vU3RhdHNTdWJzZXQsIGl4ID0gbGV2ZWwyX2l4KQoKYGBgCgpMb29raW5nIGF0IHRoZSBwcm9wb3J0aW9uYWwgbWVhbiBhYnNvbHV0ZSBlcnJvciAocG1hZSksIGV4cHJlc3NlZCBpbiBwZXJjZW50LCB3ZSBjYW4gc2VlIHRoYXQgaXQgZG9lc24ndCBpbXByb3ZlIG11Y2ggZm9yIHRoZSB3aG9sZSBlbnNlbWJsZSwgYnV0ICpkb2VzKiBpbXByb3ZlIHNpZ25pZmljYW50bHkgZm9yIHRoZSBzdWJzZXQgb2YgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGZhbGwgd2l0aGluIEFXJ3MgY29uc3RyYWludHMgZnJvbSB0aGUgZmlyc3QgZW5zZW1ibGUgKG1hcmtlZCAiX3N1YiIpLgoKYGBge3IsIGVjaG8gPSBUUlVFfQoKcG1hZV93YXZlMDAgPC0gbGFwcGx5KGxvb3N0YXRzX2ttX1lfbGV2ZWwxYSwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKcG1hZV93YXZlMDEgPC0gbGFwcGx5KGxvb3N0YXRzX2ttX1lfbGV2ZWwxYV93YXZlMDEsIEZVTiA9IGZ1bmN0aW9uKHgpIHgkcG1hZSApCgpwbWFlX3dhdmUwMF9zdWIgPC0gbGFwcGx5KGxvb3N0YXRzX2ttX1lfbGV2ZWwxYV9zdWIsIEZVTiA9IGZ1bmN0aW9uKHgpIHgkcG1hZSApCnBtYWVfd2F2ZTAxX3N1YiA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMV9zdWIsIEZVTiA9IGZ1bmN0aW9uKHgpIHgkcG1hZSApCgpwbWFlX3RhYmxlIDwtIGNiaW5kKHBtYWVfd2F2ZTAwLCBwbWFlX3dhdmUwMSwgcG1hZV93YXZlMDBfc3ViLCBwbWFlX3dhdmUwMV9zdWIpCgpwcmludChwbWFlX3RhYmxlKQoKYGBgCgojIENvbXBhcmluZyBhdG1vc3BoZXJpYyBncm93dGggaW4gd2F2ZTAwLCB3YXZlMDEgYW5kIG9ic2VydmF0aW9ucwoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0IDwtIHJlYWRfZXhjZWwoJ0dsb2JhbF9DYXJib25fQnVkZ2V0XzIwMjB2MS4wLnhsc3gnLCBzaGVldCA9ICJIaXN0b3JpY2FsIEJ1ZGdldCIsIHNraXAgPSAxNSwgbl9tYXggPSAyNzApCgpyZXNpZCA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYG9jZWFuIHNpbmtgIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmAKCnJlc2lkX2l4IDwtIHdoaWNoKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyICVpbiUgeWVhcnMpCgpuZWdfYWcgPC0gc3dlZXAobmJwX2VucywgMiwgcmVzaWRbcmVzaWRfaXhdLCBGVU4gPSAnKycpCgoKbWF0cGxvdCh5ZWFycywgdCgtbmVnX2FnKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0gYygwLCA4KSwgY29sID0gd2F2ZTAwY29sLCAKICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2F0bW9zcGhlcmljIGdyb3d0aCcsIHhsYWIgPSAnJywKICAgICAgICAgYnR5ID0gJ24nLCB4bGltID0gYygxOTYwLCAyMDIwKSkKCmxpbmVzKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyLCBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGAsIGNvbCA9ICdyZWQnKQoKCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQoKb2JzY29sID0gJ3B1cnBsZScKd2F2ZTAxX2xldmVsMmNvbCA8LSAnZ29sZCcKCmFnIDwtIG1hdHJpeChucm93ID0gbnJvdyhuYnBfZW5zKSwgbmNvbCA9IG5jb2wobmJwX2VucykpCgphZzAxIDwtIG1hdHJpeChucm93ID0gbnJvdyhuYnBfZW5zX3dhdmUwMSksIG5jb2wgPSBuY29sKG5icF9lbnNfd2F2ZTAxKSkKCmZvcihpIGluIDE6bnJvdyhuYnBfZW5zKSl7CiAgCmFnW2ksIF0gPC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmBbcmVzaWRfaXhdIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBvY2VhbiBzaW5rYFtyZXNpZF9peF0gLSAgbmJwX2Vuc1tpLCBdCiAgCn0KCgpmb3IoaSBpbiAxOm5yb3cobmJwX2Vuc193YXZlMDEpKXsKYWcwMVtpLCBdIDwtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgZm9zc2lsIGVtaXNzaW9ucyBleGNsdWRpbmcgY2FyYm9uYXRpb25gW3Jlc2lkX2l4XSAtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgb2NlYW4gc2lua2BbcmVzaWRfaXhdIC0gIG5icF9lbnNfd2F2ZTAxW2ksIF0KICAKfQoKYWdfc3RhbiA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGZvc3NpbCBlbWlzc2lvbnMgZXhjbHVkaW5nIGNhcmJvbmF0aW9uYFtyZXNpZF9peF0gLSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYG9jZWFuIHNpbmtgW3Jlc2lkX2l4XSAtICBuYnBfc3RhbgoKI3BkZih3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGZpbGUgPSAnYWcucGRmJykKbWF0cGxvdCh5ZWFycywgdChhZyksIHR5cGUgPSAnbCcsIGx0eSA9ICdzb2xpZCcseWxpbSA9IGMoLTIsIDEwKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxMDApLCAKICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2F0bW9zcGhlcmljIGdyb3d0aCcsIHhsYWIgPSAnJywKICAgICAgICAgYnR5ID0gJ24nLCB4bGltID0gYygxOTYwLCAyMDEzKSkKCm1hdGxpbmVzKHllYXJzLCB0KGFnMDEpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDEwMCksIGx0eSA9ICdzb2xpZCcpCgptYXRsaW5lcyh5ZWFycywgdChhZzAxW2xldmVsMl9peF93YXZlMDEsIF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxX2xldmVsMmNvbCwgMTAwKSAsIGx0eSA9ICdzb2xpZCcpCgpsaW5lcyhoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBhdG1vc3BoZXJpYyBncm93dGhgLCBjb2wgPSBvYnNjb2wsIGx3ZCA9MikKCmxpbmVzKHllYXJzLCBhZ19zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPTIpCgoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnLCAnR0NCJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wsIHdhdmUwMV9sZXZlbDJjb2wsIHN0YW5jb2wsIG9ic2NvbCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCiNkZXYub2ZmKCkKCmBgYAoKCiMjIEFuZHkgYXNrcyAtIHdoYXQgY29uc3RyYWludCBkb2VzIHRoYXQgZ2l2ZSB1cyBpbiBjdW11bGF0aXZlIE5CUD8KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KCmN1bXVsYXRpdmVfbmJwX2VucyA8LSB0KGFwcGx5KG5icF9lbnMsIDEsIGN1bXN1bSkpCgpjdW11bGF0aXZlX25icF9lbnNfd2F2ZTAxIDwtIHQoYXBwbHkobmJwX2Vuc193YXZlMDEsIDEsIGN1bXN1bSkpCgpjdW11bGF0aXZlX25icF9zdGFuIDwtIGN1bXN1bShuYnBfc3RhbikKCm1hdHBsb3QoeWVhcnMsIHQoY3VtdWxhdGl2ZV9uYnBfZW5zKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0gYygtMTMwLCAyNTApLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIAogICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnY3VtdWxhdGl2ZSBOQlAnLCB4bGFiID0gJycsCiAgICAgICAgIGJ0eSA9ICduJywgeGxpbSA9IGMoMTg1MCwgMjAyMCkpCgptYXRsaW5lcyh5ZWFycywgdChjdW11bGF0aXZlX25icF9lbnNfd2F2ZTAxKSwKICAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApLAogICAgICAgICBsdHkgPSAnc29saWQnKQoKbWF0bGluZXMoeWVhcnMsIHQoY3VtdWxhdGl2ZV9uYnBfZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdKSwKICAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMV9sZXZlbDJjb2wsIDE1MCksCiAgICAgICAgIGx0eSA9ICdzb2xpZCcpCgoKbGluZXMoeWVhcnMsIGN1bXVsYXRpdmVfbmJwX3N0YW4sIGNvbCA9IG1ha2VUcmFuc3BhcmVudChzdGFuY29sLCAyMDApLCBsdHkgPSAnc29saWQnLCBsd2QgPSAxLjUgKQoKCmxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsICd3YXZlMDEnLCAnd2F2ZTAxIGNvbnN0cmFpbmVkJywgJ3N0YW5kYXJkJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wsIHdhdmUwMV9sZXZlbDJjb2wsIHN0YW5jb2wpLCBsdHkgPSAnc29saWQnLCBsd2QgPSAxLjUpCgpgYGAKCiMjIEVkZHkgc3VnZ2VzdHMgbWVhc3VyaW5nIGN1bXVsYXRpdmUgTkJQIGFnYWluc3QgYXRtb3NwaGVyaWMgZ3Jvd3RoIHJhdGUKCmBgYHtyfQoKIyBDdW11bGF0aXZlIE5CUCBhdCB0aGUgZW5kIG9mIHRoZSBydW4KY25icF9tb2Rlcm5fZW5zIDwtIGFwcGx5KGN1bXVsYXRpdmVfbmJwX2Vuc1ssIDEzNToxNjRdLCAxLCBtZWFuKQoKY25icF9tb2Rlcm5fd2F2ZTAxIDwtIGFwcGx5KGN1bXVsYXRpdmVfbmJwX2Vuc193YXZlMDFbLCAxMzU6MTY0XSwgMSwgbWVhbikKCmNuYnBfbW9kZXJuX3N0YW4gPC0gbWVhbihjdW11bGF0aXZlX25icF9zdGFuWzEzNToxNjRdKQoKCgpgYGAKCkNhbGN1bGF0ZSB0aGUgYXRtb3NwaGVyaWMgZ3Jvd3RoIHJhdGUgb2YgMTk4NC0gMjAxMyB1c2luZyBhIHNpbXBsZSBsaW5lYXIgZml0CgpgYGB7cn0KIyBhZ3IgPSBhdG1vc3BoZXJpYyBncm93dGggcmF0ZQojIGFnaWF2ID0gYXRtb3NwaGVyaWMgZ3Jvd3RoIGludGVyYW5udWFsIHZhcmlhYmlsaXR5CgphZ3JfbW9kZXJuX2VucyA8LSByZXAoTkEsIGxlbmd0aCA9IG5yb3coYWcpKQphZ2lhdl9tb2Rlcm5fZW5zIDwtIHJlcChOQSwgbGVuZ3RoID0gbnJvdyhhZykpCgpmb3IoaSBpbiAxOm5yb3coYWcpKXsKICAKICBkYXQgPC0gZGF0YS5mcmFtZSh0ID0xOjMwLCBhZyA9ICBhZ1tpLCAxMzU6MTY0XSkKICBmaXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KQogIGFncl9tb2Rlcm5fZW5zW2ldIDwtIGNvZWYoZml0KVsndCddCiAgYWdpYXZfbW9kZXJuX2Vuc1tpXSA8LSBzZChmaXQkcmVzaWR1YWxzKQp9CgoKYWdyX21vZGVybl93YXZlMDEgPC0gcmVwKE5BLCBsZW5ndGggPSBucm93KGFnMDEpKQphZ2lhdl9tb2Rlcm5fd2F2ZTAxIDwtIHJlcChOQSwgbGVuZ3RoID0gbnJvdyhhZzAxKSkKCmZvcihpIGluIDE6bnJvdyhhZzAxKSl7CiAgCiAgZGF0IDwtIGRhdGEuZnJhbWUodCA9MTozMCwgYWcgPSAgYWcwMVtpLCAxMzU6MTY0XSkKICBmaXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KQogIGFncl9tb2Rlcm5fd2F2ZTAxW2ldIDwtIGNvZWYoZml0KVsndCddCiAgYWdpYXZfbW9kZXJuX3dhdmUwMVtpXSA8LSBzZChmaXQkcmVzaWR1YWxzKQp9CgpkYXQgPC0gZGF0YS5mcmFtZSh0ID0xOjMwLCBhZyA9ICBhZ19zdGFuWzEzNToxNjRdKQphZ3Jfc3Rhbl9maXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KSAKYWdyX3N0YW4gPC0gY29lZihhZ3Jfc3Rhbl9maXQpWyd0J10KYWdpYXZfc3RhbiA8LSBzZChhZ3Jfc3Rhbl9maXQkcmVzaWR1YWxzKQogIAogIApgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KCnBsb3QoYWdyX21vZGVybl9lbnMsIGNuYnBfbW9kZXJuX2VucywgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwKICAgICB5bGltID0gYygtMTIwLDIyMCksIHBjaCA9IDE5LAogICAgIHhsYWIgPSAnQXRtb3NwaGVyaWMgZ3Jvd3RoIHJhdGUgMTk4NC0yMDE0JywKICAgICB5bGFiID0gJ0N1bXVsYXRpdmUgTkJQIGZyb20gcHJlaW5kdXN0cmlhbCBieSAxOTg0LTIwMTMnCiAgICAgKQoKY25icF93YXZlMDFfY29scyA8LSByZXAod2F2ZTAxY29sLCBsZW5ndGgoY25icF9tb2Rlcm5fd2F2ZTAxKSkKY25icF93YXZlMDFfY29sc1tsZXZlbDJfaXhfd2F2ZTAxXSA8LSB3YXZlMDFfbGV2ZWwyY29sIAoKcG9pbnRzKCBhZ3JfbW9kZXJuX3dhdmUwMSxjbmJwX21vZGVybl93YXZlMDEsIGNvbCA9IG1ha2VUcmFuc3BhcmVudChjbmJwX3dhdmUwMV9jb2xzLCAxNTApLCBwY2ggPSAxOSkKcG9pbnRzKCBhZ3Jfc3RhbixjbmJwX21vZGVybl9zdGFuLCBjb2wgPSBzdGFuY29sLCBwY2ggPSAxOSwgY2V4ID0gMS41KQoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCksIHBjaCA9IDE5KQoKcHJpbnQoYygnY29ycmVsYXRpb24gYWdyIHZzIGNuYnAgKGFsbCBtZW1iZXJzKScsIGNvcihhZ3JfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zKSkpCgpwcmludChjKCdjb3JyZWxhdGlvbiBhZ3IgdnMgY25icCAod2F2ZTAxKScsIGNvcihhZ3JfbW9kZXJuX3dhdmUwMSwgY25icF9tb2Rlcm5fd2F2ZTAxKSkpCgpmaXRfd2F2ZTAwIDwtIGxtKGFncl9tb2Rlcm5fZW5zIH4gY25icF9tb2Rlcm5fZW5zKQpmaXRfd2F2ZTAxIDwtIGxtKGFncl9tb2Rlcm5fd2F2ZTAxIH4gY25icF9tb2Rlcm5fd2F2ZTAxKQoKcHJpbnQoc3VtbWFyeShmaXRfd2F2ZTAwKSkKcHJpbnQoc3VtbWFyeShmaXRfd2F2ZTAxKSkKCmBgYApJbnRlcmFubnVhbCB2YXJpYWJpbGl0eSBhbmQgY3VtdWxhdGl2ZSBOQlAgIAoKKGNvcnJlbGF0aW9ucyBhcmUgY2xvc2UgdG8gemVybywgZXNwZWNpYWxseSBpbiB0aGUgbGF0ZXIgd2F2ZSkKCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CgpwbG90KGFnaWF2X21vZGVybl9lbnMsIGNuYnBfbW9kZXJuX2VucywgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwKICAgICB5bGltID0gYygtMTIwLDIyMCksIHBjaCA9IDE5LAogICAgIHhsYWIgPSAnQXRtb3NwaGVyaWMgZ3Jvd3RoIElBViAxOTg0LTIwMTQnLAogICAgIHlsYWIgPSAnQ3VtdWxhdGl2ZSBOQlAgZnJvbSBwcmVpbmR1c3RyaWFsIGJ5IDE5ODQtMjAxMycKICAgICApCgpwb2ludHMoIGFnaWF2X21vZGVybl93YXZlMDEsIGNuYnBfbW9kZXJuX3dhdmUwMSwgY29sID0gbWFrZVRyYW5zcGFyZW50KGNuYnBfd2F2ZTAxX2NvbHMsIDE1MCksIHBjaCA9IDE5KQpwb2ludHMoIGFnaWF2X3N0YW4sY25icF9tb2Rlcm5fc3RhbiwgY29sID0gc3RhbmNvbCwgcGNoID0gMTksIGNleCA9IDEuNSkKCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sLCB3YXZlMDFfbGV2ZWwyY29sLCBzdGFuY29sKSwgcGNoID0gMTkpCgpgYGAKCgpgYGB7cn0KCgpwcmludChjb3IoYWdpYXZfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zKSkKcHJpbnQoY29yKCBhZ2lhdl9tb2Rlcm5fd2F2ZTAxLCBjbmJwX21vZGVybl93YXZlMDEpKQoKYGBgCgoKIyMgSG93IGNsb3NlIGNhbiB3ZSBnZXQgdGhlIG1vZGVsIHRvIHJlYWxpdHk/CgpVc2luZyBBdG1vc3BoZXJpYyBHcm93dGggUmF0ZSBhcyBhbiBleGFtcGxlLCBob3cgY2xvc2UgY2FuIHdlIGdldCB0aGUgbW9kZWwgdG8gb2JzZXJ2YXRpb25zPyBDYW4gd2UgZG8gYmV0dGVyIHRoYW4gc3RhbmRhcmQ/IFdoYXQgYXJlIHRoZSB0cmFkZSBvZmZzIG9mIGRvaW5nIHNvPyBIb3cgZG9lcyBnZXR0aW5nIGNsb3NlIGluIEFHUiBhZmZlY3QgcGVyZm9ybWFuY2UgaW4gb3RoZXIgb3V0cHV0cz8KCmBgYHtyfQojIERlZmluZSB0aGUgb2JzZXJ2ZWQgYXRtb3NwaGVyaWMgZ3Jvd3RoIHJhdGUuCgphZ19vYnMgPC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBhdG1vc3BoZXJpYyBncm93dGhgW3doaWNoKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyICVpbiUgeWVhcnMpXQoKIyBNb2RlbCBkZXBhcnR1cmUgZnJvbSBvYnNlcnZhdGlvbnMgb2YgYXRtb3NwaGVyaWMgZ3Jvd3RoCmFnX2VyciA8LSBzd2VlcChhZywgMiwgYWdfb2JzLCBGVU4gPSAnLScpCmFnMDFfZXJyIDwtIHN3ZWVwKGFnMDEsIDIsIGFnX29icywgRlVOID0gJy0nKQoKCmxvbmdfbW9kZXJuX3llYXJzIDwtIDE5NjA6MjAxMwphZ19tb2Rlcm5faXggPC0gd2hpY2goIHllYXJzICVpbiUgbG9uZ19tb2Rlcm5feWVhcnMpCgphZ19lcnJfbW9kZXJuIDwtIGFnX2VyclssIGFnX21vZGVybl9peF0KYWcwMV9lcnJfbW9kZXJuIDwtIGFnMDFfZXJyWywgYWdfbW9kZXJuX2l4XQoKCmFnX2Vycl9zdGFuIDwtIGFnX3N0YW4gLSBhZ19vYnMKYWdfZXJyX3N0YW5fbW9kZXJuIDwtIGFnX2Vycl9zdGFuW2FnX21vZGVybl9peF0KCmBgYAoKYGBge3J9Cm1hdHBsb3QobG9uZ19tb2Rlcm5feWVhcnMsIHQoYWdfZXJyX21vZGVybiksIHR5cGUgPSAnbCcsIGx0eSA9ICdzb2xpZCcsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDEwMCksIG1haW4gPSAnQW1vc3BoZXJpYyBHcm93dGggRXJyb3InKQoKZXJyY29sIDwtIHJlcCh3YXZlMDFjb2wsIG5yb3coYWcwMSkpCiNlcnJjb2xbbGV2ZWwyX2l4X3dhdmUwMV0gPC0gd2F2ZTAxX2xldmVsMmNvbAptYXRsaW5lcyhsb25nX21vZGVybl95ZWFycywgdChhZzAxX2Vycl9tb2Rlcm4pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoZXJyY29sLCAxMDApLCBsdHkgPSAnc29saWQnKQptYXRsaW5lcyhsb25nX21vZGVybl95ZWFycywgdChhZzAxX2Vycl9tb2Rlcm5bbGV2ZWwyX2l4X3dhdmUwMSwgXSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFfbGV2ZWwyY29sLDEwMCksIGx0eSA9ICdzb2xpZCcpCgpsaW5lcyhsb25nX21vZGVybl95ZWFycywgYWdfZXJyX3N0YW5fbW9kZXJuLCBjb2wgPSBzdGFuY29sKQoKbGVnZW5kKCdib3R0b21sZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDEuNSkKCmFibGluZShoPTApCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CgojIEF0bW9zcGhlcmljIGdyb3d0aCBtZWFuIGVycm9yCmFnX21vZGVybl9tZSA8LSBhcHBseShhZ19lcnJfbW9kZXJuLDEsbWVhbikKYWcwMV9tb2Rlcm5fbWUgPC0gYXBwbHkoYWcwMV9lcnJfbW9kZXJuLDEsIG1lYW4pCmFnX21vZGVybl9tZV9zdGFuIDwtIG1lYW4oYWdfZXJyX3N0YW5bYWdfbW9kZXJuX2l4XSkKCgojIE1lYW4gYWJzb2x1dGUgZXJyb3IKYWdfbW9kZXJuX21hZSA8LSBhcHBseShhYnMoYWdfZXJyX21vZGVybiksMSxtZWFuKQphZzAxX21vZGVybl9tYWUgPC0gYXBwbHkoYWJzKGFnMDFfZXJyX21vZGVybiksMSwgbWVhbikKYWdfbW9kZXJuX21hZV9zdGFuIDwtIG1lYW4oYWJzKGFnX2Vycl9zdGFuW2FnX21vZGVybl9peF0pKQoKCgojIFJvb3QgbWVhbiBzcXVhcmUgZXJyb3IKYWdfbW9kZXJuX3Jtc2UgPC0gYXBwbHkoYWdfZXJyX21vZGVybiwxLCBmdW5jdGlvbih4KSBzcXJ0KG1lYW4oeF4yKSkpCmFnMDFfbW9kZXJuX3Jtc2UgPC0gYXBwbHkoYWcwMV9lcnJfbW9kZXJuLDEsIGZ1bmN0aW9uKHgpIHNxcnQobWVhbih4XjIpKSkKYWdfbW9kZXJuX3Jtc2Vfc3RhbiA8LSBzcXJ0KG1lYW4oYWdfZXJyX3N0YW5bYWdfbW9kZXJuX2l4XV4yKSkKCiMgVGhlcmUgYXJlIHNvbWUgYmlnIG91dGxpZXJzIApvdXRsaWVyX2l4X3dhdmUwMSA8LSB3aGljaChhYnMoYWcwMV9tb2Rlcm5fbWUgKT4gMTAwKQoKcGFyKG1mcm93ID0gYygyLDEpKQpoaXN0KGFnX21vZGVybl9tZSwgbWFpbiA9ICdBdG1vc3BoZXJpYyBncm93dGggbWVhbiBlcnJvcicsIGNvbCA9IHdhdmUwMGNvbCwgeGxpbSA9IGMoLTMsIDMpKQpydWcoYWdfbW9kZXJuX21lX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIpCmhpc3QoYWcwMV9tb2Rlcm5fbWVbLW91dGxpZXJfaXhfd2F2ZTAxXSwgY29sID0gd2F2ZTAxY29sLCB4bGltID0gYygtMywzKSwgbWFpbiA9ICcnKQpydWcoYWdfbW9kZXJuX21lX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIpCgpwYXIobWZyb3cgPSBjKDIsMSkpCmhpc3QoYWdfbW9kZXJuX21hZSwgbWFpbiA9ICdBdG1vc3BoZXJpYyBncm93dGggbWVhbiBhYnNvbHV0ZSBlcnJvcicsIGNvbCA9IHdhdmUwMGNvbCwgeGxpbSA9IGMoMCwgMykpCnJ1ZyhhZ19tb2Rlcm5fbWFlX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIpCmhpc3QoYWcwMV9tb2Rlcm5fbWFlWy1vdXRsaWVyX2l4X3dhdmUwMV0sIGNvbCA9IHdhdmUwMWNvbCwgbHdkID0gMiwgeGxpbSA9IGMoMCwzKSwgbWFpbiA9ICcnKQpydWcoYWdfbW9kZXJuX21hZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyKQoKcGFyKG1mcm93ID0gYygyLDEpKQpoaXN0KGFnX21vZGVybl9ybXNlLCB4bGltID0gYygwLDMpLCBjb2wgPSB3YXZlMDBjb2wsIG1haW4gPSAnQXRtb3NwaGVyaWMgZ3Jvd3RoIFJNU0UnKQpydWcoYWdfbW9kZXJuX3Jtc2Vfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMiApCmhpc3QoYWcwMV9tb2Rlcm5fcm1zZVstb3V0bGllcl9peF93YXZlMDFdLCBjb2wgPSB3YXZlMDFjb2wsIHhsaW0gPSBjKDAsMyksIG1haW4gPSAnJykKcnVnKGFnX21vZGVybl9ybXNlX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIgKQoKCgpgYGAKCgpXZSd2ZSBlc3RhYmxpc2hlZCB0aGF0IG1vc3Qgb2YgdGhlIG9yaWdpbmFsIGVuc2VtYmxlIGhhdmUgYW4gTUUvTUFFL1JNU0UgbGFyZ2VyIHRoYW4gdGhlIHN0YW5kYXJkIHJ1bi4gTW9yZSAoYnV0IGZldykgb2YgdGhlIHdhdmUwMSBwZXJmb3JtIGJldHRlciB0aGFuIHN0YW5kYXJkLiAKCmBgYHtyfQoKCmJldHRlcl9peF9hZ19ybXNlIDwtIHdoaWNoKGFnX21vZGVybl9ybXNlIDwgYWdfbW9kZXJuX3Jtc2Vfc3RhbikKYmV0dGVyX2l4X2FnMDFfcm1zZSA8LSB3aGljaChhZzAxX21vZGVybl9ybXNlIDwgYWdfbW9kZXJuX3Jtc2Vfc3RhbikKCgpYX2JldHRlcl9hZyA8LSByYmluZChYW2JldHRlcl9peF9hZ19ybXNlLCBdLCBYX3dhdmUwMV90cmFpbltiZXR0ZXJfaXhfYWcwMV9ybXNlLCBdKQoKYGBgCgoKQSBtYXAgb2YgdGhlIDJEIHByb2plY3Rpb25zIG9mIHBhcmFtZXRlciBzcGFjZSB3aGVyZSB0aGUgZW5zZW1ibGUgbWVtYmVyIHBlcmZvcm1zIGJldHRlciB0aGFuIHN0YW5kYXJkLiAgCgpUaGUgYmx1ZSBwYXJ0IGlzIHRoZSBmaXJzdCB3YXZlLCBhbmQgbm90IHN1YmplY3QgdG8gY29uc3RyYWludCBzbyBtYXkgYmUgcmVtb3ZlZCBpbiB0aGUgc2Vjb25kIHdhdmUgKHdhdmUwMSkuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CgpiZXR0ZXJfaXggPC0gMTpucm93KFhfYmV0dGVyX2FnKQoKYmV0dGVyX2NvbHMgPC0gYyhyZXAod2F2ZTAwY29sLCBsZW5ndGgoYmV0dGVyX2l4X2FnX3Jtc2UpKSwgcmVwKHdhdmUwMWNvbCxsZW5ndGgoYmV0dGVyX2l4X2FnMDFfcm1zZSkpKQoKcGFpcnMoWF9iZXR0ZXJfYWcsCiAgICAgIGNvbCA9IGJldHRlcl9jb2xzLAogICAgICBnYXAgPSAwLAogICAgICB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwY2ggPSAxOSwKICAgICAgY2V4PSAwLjgsCiAgICAgIGxvd2VyLnBhbmVsID0gTlVMTCkKCgoKYGBgCgojIyBCdWlsZCBlbXVsYXRvcnMgYW5kIGZpbmQgcGFydHMgb2YgcGFyYW1ldGVyIHNwYWNlIHRoYXQgdG8gYmV0dGVyIHRoYW4gc3RhbmRhcmQgYXQgYXRtb3NwaGVyaWMgZ3Jvd3RoLgoKSGF2aW5nIHRyb3VibGUgZml0dGluZyBSTVNFLCB0byB0cnlpbmcgbWVhbiBlcnJvci4KCldoeSBpcyB0aGVyZSBhbiBvZGQgY29sbGVjdGlvbiBhdCBqdXN0IHVuZGVyIDE/CgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KIyB0aGVyZSBhcmUgMTAwIHdhdmUwMSBlbnNlbWJsZSBtZW1iZXJzIHRoYXQgcGFzcyB0aGUgbGV2ZWwgMiBjb25zdHJhaW5zdApsZW5ndGgobGV2ZWwyX2l4X3dhdmUwMSkKCmJldHRlcl9peF9hZ19tZSA8LSB3aGljaChhYnMoYWdfbW9kZXJuX21lKSA8IGFicyhhZ19tb2Rlcm5fbWVfc3RhbikpCgojIFRoZXJlIGFyZSAxMDQgdGhhdCBoYXZlIGEgc21hbGxlciBtZWFuIGVycm9yIHRoYW4gc3RhbmRhcmQKYmV0dGVyX2l4X2FnMDFfbWUgPC0gd2hpY2goYWJzKGFnMDFfbW9kZXJuX21lKSA8IGFicyhhZ19tb2Rlcm5fbWVfc3RhbikpCgojIFRoZXJlIGFyZSBvbmx5IDQxIHRoYXQgcGFzcyBsZXZlbCAyIEFORCBoYXZlIHNtYWxsZXIgZXJyb3IgdGhhbiBzdGFuZGFyZApsZXZlbDJfYW5kX2JldHRlcl9hZzAxX2l4IDwtIGludGVyc2VjdChsZXZlbDJfaXhfd2F2ZTAxLCBiZXR0ZXJfaXhfYWcwMV9tZSkKCmBgYAoKVGhpcyBuZXh0IHBhaXJzIHBsb3QgbG9va3MgYXQgYWxsIHRoZSBlbnNlbWJsZSBtZW1iZXJzIHRoYXQgaGF2ZSBhIGJldHRlciBtZWFuIGF0bW9zcGhlcmljIGdyb3d0aCBlcnJvciB0aGFuIHN0YW5kYXJkLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9ClhfYmV0dGVyX2FnIDwtIHJiaW5kKFhbYmV0dGVyX2l4X2FnX21lLCBdLCBYX3dhdmUwMV90cmFpbltiZXR0ZXJfaXhfYWcwMV9tZSwgXSkKCmJldHRlcl9peCA8LSAxOm5yb3coWF9iZXR0ZXJfYWcpCgpiZXR0ZXJfY29scyA8LSBjKG1ha2VUcmFuc3BhcmVudChyZXAod2F2ZTAwY29sLCBsZW5ndGgoYmV0dGVyX2l4X2FnX21lKSksIDEwMCksIG1ha2VUcmFuc3BhcmVudChyZXAod2F2ZTAxY29sLGxlbmd0aChiZXR0ZXJfaXhfYWcwMV9tZSkpLCAxMDApKQoKcGFpcnMoWF9iZXR0ZXJfYWcsCiAgICAgIGNvbCA9IGJldHRlcl9jb2xzLAogICAgICBnYXAgPSAwLAogICAgICB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwY2ggPSAxOSwKICAgICAgY2V4PSAwLjgsCiAgICAgIGxvd2VyLnBhbmVsID0gTlVMTCkKYGBgCgpUaGlzIG5leHQgcGxvdCBsb29rcyBhdCBhbGwgdGhlIGVuc2VtYmxlIG1lbWJlcnMgdGhhdCBoYXZlIGEgYmV0dGVyIG1lYW4gYXRtb3NwaGVyaWMgZ3Jvd3RoIGVycm9yIHRoYW4gc3RhbmRhcmQgQU5EIHBhc3MgdGhlIGxldmVsIDIgY29uc3RyYWludHMuICAKClRoZSBudW1iZXIgaXMgc21hbGwgKDQxLzMwMCksIGJ1dCB0aGUgZW5zZW1ibGUgbWVtYmVycyBzZWVtIHNwcmVhZCBhY3Jvc3MgcGFyYW1ldGVyIHNwYWNlLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CgpYX2xldmVsMl9hbmRfYmV0dGVyX2FnMDEgPC0gWFtsZXZlbDJfYW5kX2JldHRlcl9hZzAxX2l4LCBdCnBhaXJzKFhfbGV2ZWwyX2FuZF9iZXR0ZXJfYWcwMSwKICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KCdibGFjaycsIDE1MCksCiAgICAgIGdhcCA9IDAsCiAgICAgIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBjaCA9IDE5LAogICAgICBjZXg9IDAuOCwKICAgICAgbG93ZXIucGFuZWwgPSBOVUxMKQoKYGBgCgoKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoRGljZUtyaWdpbmcpCgphZzAxX291dGxpZXJfaXggPC0gd2hpY2goYWcwMV9tb2Rlcm5fbWUgPiAxMCkKCmZpdF9hZ19tZSA8LSBrbSh+LiwgZGVzaWduID0gWF93YXZlMDFfdHJhaW5bLWFnMDFfb3V0bGllcl9peCwgXSwgcmVzcG9uc2UgPSBhZzAxX21vZGVybl9tZVstYWcwMV9vdXRsaWVyX2l4XSwgCiAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KG1heGl0ID0gMjAwKSkKCmBgYAoKYGBge3J9CgpwbG90KGZpdF9hZ19tZSkKCgoKCmBgYAoKYGBge3J9CmxpYnJhcnkoZW10b29scykKbGlicmFyeShpbXB0b29scykKbGlicmFyeSh2aXp0b29scykKCm51bmlmIDwtIDEwMDAwMApYX3VuaWYgPC0gc2FtcF91bmlmKG51bmlmLCBtaW5zID0gcmVwKDAsMzIpLCBtYXhlcyA9IHJlcCgxLCAzMikpCmNvbG5hbWVzKFhfdW5pZikgPC0gY29sbmFtZXMoWCkKCnByZWRfdW5pZl9hZyA8LSBwcmVkaWN0LmttKGZpdF9hZ19tZSwgbmV3ZGF0YSA9IFhfdW5pZiwgdHlwZSA9ICdVSycgKSAKCiMgS2VlcHMgYWJvdXQgMjAlClhfa2VwdF9peCA8LSB3aGljaChhYnMocHJlZF91bmlmX2FnJG1lYW4pIDwgYWJzKGFnX21vZGVybl9tZV9zdGFuKSkKCgoobGVuZ3RoKFhfa2VwdF9peCkgLyBudW5pZikgKiAxMDAKIyB0aGlzIGlzIDglCiNYX2tlcHRfaXggPC0gd2hpY2goYWJzKHByZWRfdW5pZiRtZWFuKSA8IDAuMSkKCgpgYGAKCgojIyBJbnB1dCBzcGFjZSB3aXRoIGxvdyBBdG1vc3BoZXJpYyBHcm93dGggRXJyb3IKVGhpcyBwYWlycyBwbG90IHNob3dzIHRoZSAyZCBhbmQgbWFyZ2luYWwgZGVuc2l0eSBvZiBlbXVsYXRlZCBpbnB1dCBwb2ludHMgd2hlcmUgdGhlIGVtdWxhdGVkIGF0bW9zcGhlcmljIGdyb3d0aCBpcyBjbG9zZXIgdG8gdGhlIG9ic2VydmF0aW9ucyB0aGFuIHRoZSBzdGFuZGFyZCBtb2RlbC4KClRoaXMgdGVjaG5pcXVlIG1pZ2h0IHByb3ZpZGUgYSB1c2VmdWwgc2V0IG9mIHBvaW50cyBmb3Igb3B0aW1pc2luZyB0aGUgbW9kZWwgKGF0IGxlYXN0IHRvIGF0bW9zcGhlcmljIGdyb3d0aCkuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMiwgd2FybmluZyA9IEZBTFNFfQojcGFpcnMoWF91bmlmW1hfa2VwdF9peCwgXVsxOjUwLF0sCiMgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJyw1MCksCiMgICAgICBnYXAgPSAwLAojICAgICAgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKIyAgICAgIHBjaCA9IDE5LAojICAgICAgY2V4PSAwLjgsCiMgICAgICBsb3dlci5wYW5lbCA9IE5VTEwpCgpwYXIob21hID0gYygwLDAsMCwzKSwgYmcgPSAnd2hpdGUnKQoKCnBhbmVsLmhpc3QgPSBmdW5jdGlvbih4LCAuLi4pIHsKICB1c3IgPSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQogIHBhcih1c3IgPSBjKHVzclsxOjJdLCAwLCAxLjUpKQogIGhpc3QoeCwgZnJlcSA9IEZBTFNFLCBjb2w9ImN5YW4iLCBhZGQ9VFJVRSkgCiAgbGluZXMoZGVuc2l0eSh4KSkKfQoKcGFpcnMoWF91bmlmW1hfa2VwdF9peCwgXSwKICAgICMgIGxhYmVscyA9IDE6ZCwKICAgICAgZ2FwID0gMCwgbG93ZXIucGFuZWwgPSBOVUxMLCB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwYW5lbCA9IGRmdW5jX3VwLAogICAgICBkaWFnLnBhbmVsID0gcGFuZWwuaGlzdCwKICAgICAgY2V4LmxhYmVscyA9IDEsCiAgICAgIGNvbC5heGlzID0gJ3doaXRlJywKICAgICAgZGZ1bmNfY29sID0gcmIpCgpgYGAKCk5leHQsIGNoZWNrIGVtdWxhdG9ycyBvZiBhbGwgdGhlIG90aGVyIG91dHB1dHMgYW5kIGFwcGx5IHRoZSBjb25zdHJhaW50cyB0byB0aGVtLiBTZWUgaG93IHRoZSBjb25zdHJhaW50cyBjaGFuZ2UuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KCgojIEZpcnN0LCB0cnkgdGhlIGVtdWxhdG9ycyBidWlsdCB1c2luZyBqdXN0IHdhdmUwMQojZml0X2xpc3RfY29uc3RfbGV2ZWwxYQoKCllfY29uc3RfcHJlZF91bmlmX21lYW4gPC0gbWF0cml4KE5BLCBuY29sID0gbmNvbChZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSwgbnJvdyA9IG5yb3coWF91bmlmKSkKY29sbmFtZXMoWV9jb25zdF9wcmVkX3VuaWZfbWVhbikgPC0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCllfY29uc3RfcHJlZF91bmlmX3NkIDwtIG1hdHJpeChOQSwgbmNvbCA9IG5jb2woWV9jb25zdF9sZXZlbDFhX3NjYWxlZCksIG5yb3cgPSBucm93KFhfdW5pZikpIApjb2xuYW1lcyhZX2NvbnN0X3ByZWRfdW5pZl9zZCkgPC0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCmZvcihpIGluIDE6bGVuZ3RoKGZpdF9saXN0X2NvbnN0X2xldmVsMWEpKXsKICAKICBwcmVkX3VuaWYgPC0gcHJlZGljdC5rbShvYmplY3Q9Zml0X2xpc3RfY29uc3RfbGV2ZWwxYVtbaV1dLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJykKICAKICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWyxpIF0gPC0gcHJlZF91bmlmJG1lYW4KICBZX2NvbnN0X3ByZWRfdW5pZl9zZFssaSBdIDwtIHByZWRfdW5pZiRzZAp9CgoKYGBgCgojIyBJbnB1dCBzcGFjZSB3aXRoIGVtdWxhdGVkIG1lbWJlcnMgcGFzc2luZyBMZXZlbCAyIGNvbnN0cmFpbnRzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIHdhcm5pbmcgPSBGQUxTRX0KCmxldmVsMl9peF9lbSA8LSB3aGljaChZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgICkKCnBhaXJzKFhfdW5pZltsZXZlbDJfaXhfZW0sIF0sCiAgICAjICBsYWJlbHMgPSAxOmQsCiAgICAgIGdhcCA9IDAsIGxvd2VyLnBhbmVsID0gTlVMTCwgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGFuZWwgPSBkZnVuY191cCwKICAgICAgZGlhZy5wYW5lbCA9IHBhbmVsLmhpc3QsCiAgICAgIGNleC5sYWJlbHMgPSAxLAogICAgICBjb2wuYXhpcyA9ICd3aGl0ZScsCiAgICAgIGRmdW5jX2NvbCA9IHJiKQoKKGxlbmd0aChsZXZlbDJfaXhfZW0pIC8gbnVuaWYpICogMTAwCmBgYAoKIyMgSW5wdXQgc3BhY2Ugd2l0aCBlbXVsYXRlZCBtZW1iZXJzIHBhc3NpbmcgTGV2ZWwgMiBjb25zdHJhaW50cyBBTkQgbG93IGF0bW9zcGhlcmljIGdyb3d0aCBlcnJvcgpFbXVsYXRlZCBtZW1iZXJzIHBhc3NpbmcgbGV2ZWwyIGNvbnN0cmFpbnRzIEFORCBoYXZpbmcgbG93ZXIgZXJyb3IgaW4gYXRtb3NwaGVyaWMgZ3Jvd3RoIHRoYW4gc3RhbmRhcmQuIAoKUmVkIHBvaW50IGluZGljYXRlcyB0aGUgc3RhbmRhcmQgaW5wdXQuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMiwgd2FybmluZyA9IEZBTFNFfQoKWF9zdGFuX25vcm0gPC0gbm9ybWFsaXplKG1hdHJpeChyZXAoMSwgMzIpLCBucm93ID0gMSksIHdydCA9IGxocykKCmxldmVsMl9hbmRfYWdfaXhfZW0gPC0gd2hpY2goWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ25icF9sbmRfc3VtJ10gPiAwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbnBwX25saW1fbG5kX3N1bSddID4gMzUgJiBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbnBwX25saW1fbG5kX3N1bSddIDwgODAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPiA3NTAgJiBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1NvaWxfbG5kX3N1bSddIDwgMzAwMCAmCiAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjVmVnX2xuZF9zdW0nXSA+IDMwMCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjVmVnX2xuZF9zdW0nXSA8IDgwMCAmCiAgICAgICAgICAgICAgICAgICAgYWJzKHByZWRfdW5pZl9hZyRtZWFuKSA8IGFicyhhZ19tb2Rlcm5fbWVfc3RhbikKICAgICAgICAgICAgICAgICAgKQoKCgpwYWlycyhyYmluZChYX3VuaWZbbGV2ZWwyX2FuZF9hZ19peF9lbSwgXSwgWF9zdGFuX25vcm0pLAogICAgIyAgbGFiZWxzID0gMTpkLAogICAgICBnYXAgPSAwLCBsb3dlci5wYW5lbCA9IE5VTEwsIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBhbmVsID0gZGZ1bmNfdXBfdHJ1dGgsCiAgICAgIGRpYWcucGFuZWwgPSBwYW5lbC5oaXN0LAogICAgICBjZXgubGFiZWxzID0gMSwKICAgICAgY29sLmF4aXMgPSAnd2hpdGUnLAogICAgICBkZnVuY19jb2wgPSByYikKCgoobGVuZ3RoKGxldmVsMl9hbmRfYWdfaXhfZW0pIC8gbnVuaWYpICogMTAwCmBgYAoKCgoK